1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1982-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
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 [-Aprs] [-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	<ctype.h>
32 #include	"defs.h"
33 #include	"variables.h"
34 #include	"lexstates.h"
35 #include	"io.h"
36 #include	"name.h"
37 #include	"builtins.h"
38 #include	"history.h"
39 #include	"terminal.h"
40 #include	"edit.h"
41 
42 #define	R_FLAG	1	/* raw mode */
43 #define	S_FLAG	2	/* save in history file */
44 #define	A_FLAG	4	/* read into array */
45 #define N_FLAG	8	/* fixed size read at most */
46 #define NN_FLAG	0x10	/* fixed size read exact */
47 #define V_FLAG	0x20	/* use default value */
48 #define D_FLAG	8	/* must be number of bits for all flags */
49 
50 int	b_read(int argc,char *argv[], void *extra)
51 {
52 	Sfdouble_t sec;
53 	register char *name;
54 	register int r, flags=0, fd=0;
55 	register Shell_t *shp = (Shell_t*)extra;
56 	long timeout = 1000*shp->st.tmout;
57 	int save_prompt;
58 	static char default_prompt[3] = {ESC,ESC};
59 	NOT_USED(argc);
60 	while((r = optget(argv,sh_optread))) switch(r)
61 	{
62 	    case 'A':
63 		flags |= A_FLAG;
64 		break;
65 	    case 't':
66 		sec = sh_strnum(opt_info.arg, (char**)0,1);
67 		timeout = sec ? 1000*sec : 1;
68 		break;
69 	    case 'd':
70 		if(opt_info.arg && *opt_info.arg!='\n')
71 		{
72 			char *cp = opt_info.arg;
73 			flags &= ~((1<<D_FLAG)-1);
74 			flags |= (mbchar(cp)<< D_FLAG);
75 		}
76 		break;
77 	    case 'p':
78 		if((fd = shp->cpipe[0])<=0)
79 			errormsg(SH_DICT,ERROR_exit(1),e_query);
80 		break;
81 	    case 'n': case 'N':
82 		flags &= ~((1<<D_FLAG)-1);
83 		flags |= (r=='n'?N_FLAG:NN_FLAG);
84 		r = (int)opt_info.num;
85 		if((unsigned)r > (1<<((8*sizeof(int))-D_FLAG))-1)
86 			errormsg(SH_DICT,ERROR_exit(1),e_overlimit,"n");
87 		flags |= (r<< D_FLAG);
88 		break;
89 	    case 'r':
90 		flags |= R_FLAG;
91 		break;
92 	    case 's':
93 		/* save in history file */
94 		flags |= S_FLAG;
95 		break;
96 	    case 'u':
97 		fd = (int)opt_info.num;
98 		if(sh_inuse(fd))
99 			fd = -1;
100 		break;
101 	    case 'v':
102 		flags |= V_FLAG;
103 		break;
104 	    case ':':
105 		errormsg(SH_DICT,2, "%s", opt_info.arg);
106 		break;
107 	    case '?':
108 		errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
109 		break;
110 	}
111 	argv += opt_info.index;
112 	if(error_info.errors)
113 		errormsg(SH_DICT,ERROR_usage(2), "%s", optusage((char*)0));
114 	if(!((r=shp->fdstatus[fd])&IOREAD)  || !(r&(IOSEEK|IONOSEEK)))
115 		r = sh_iocheckfd(fd);
116 	if(fd<0 || !(r&IOREAD))
117 		errormsg(SH_DICT,ERROR_system(1),e_file+4);
118 	/* look for prompt */
119 	shp->prompt = default_prompt;
120 	if((name = *argv) && (name=strchr(name,'?')) && (r&IOTTY))
121 	{
122 		r = strlen(++name)+1;
123 		if(shp->prompt=(char*)sfreserve(sfstderr,r,SF_LOCKR))
124 		{
125 			memcpy(shp->prompt,name,r);
126 			sfwrite(sfstderr,shp->prompt,r-1);
127 		}
128 	}
129 	shp->timeout = 0;
130 	save_prompt = shp->nextprompt;
131 	shp->nextprompt = 0;
132 	r=sh_readline(shp,argv,fd,flags,timeout);
133 	shp->nextprompt = save_prompt;
134 	if(r==0 && (r=(sfeof(shp->sftable[fd])||sferror(shp->sftable[fd]))))
135 	{
136 		if(fd == shp->cpipe[0])
137 		{
138 			sh_pclose(shp->cpipe);
139 			return(1);
140 		}
141 	}
142 	sfclrerr(shp->sftable[fd]);
143 	return(r);
144 }
145 
146 /*
147  * here for read timeout
148  */
149 static void timedout(void *handle)
150 {
151 	sfclrlock((Sfio_t*)handle);
152 	sh_exit(1);
153 }
154 
155 /*
156  * This is the code to read a line and to split it into tokens
157  *  <names> is an array of variable names
158  *  <fd> is the file descriptor
159  *  <flags> is union of -A, -r, -s, and contains delimiter if not '\n'
160  *  <timeout> is number of milli-seconds until timeout
161  */
162 
163 int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeout)
164 {
165 	register int		c;
166 	register unsigned char	*cp;
167 	register Namval_t	*np;
168 	register char		*name, *val;
169 	register Sfio_t	*iop;
170 	char			*ifs;
171 	unsigned char		*cpmax;
172 	unsigned char		*del;
173 	char			was_escape = 0;
174 	char			use_stak = 0;
175 	char			was_write = 0;
176 	char			was_share = 1;
177 	int			rel, wrd;
178 	long			array_index = 0;
179 	void			*timeslot=0;
180 	int			delim = '\n';
181 	int			jmpval=0;
182 	int			size = 0;
183 	struct	checkpt		buff;
184 	if(!(iop=shp->sftable[fd]) && !(iop=sh_iostream(fd)))
185 		return(1);
186 	if(names && (name = *names))
187 	{
188 		if(val= strchr(name,'?'))
189 			*val = 0;
190 		np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME|NV_ARRAY);
191 		if((flags&V_FLAG) && shp->ed_context)
192 			((struct edit*)shp->ed_context)->e_default = np;
193 		if(flags&A_FLAG)
194 		{
195 			flags &= ~A_FLAG;
196 			array_index = 1;
197 			nv_unset(np);
198 			nv_putsub(np,NIL(char*),0L);
199 		}
200 		else
201 			name = *++names;
202 		if(val)
203 			*val = '?';
204 	}
205 	else
206 	{
207 		name = 0;
208 		if(dtvnext(shp->var_tree) || shp->namespace)
209                 	np = nv_open(nv_name(REPLYNOD),shp->var_tree,0);
210 		else
211 			np = REPLYNOD;
212 	}
213 	if(flags>>D_FLAG)	/* delimiter not new-line or fixed size read */
214 	{
215 		if(flags&(N_FLAG|NN_FLAG))
216 			size = ((unsigned)flags)>>D_FLAG;
217 		else
218 			delim = ((unsigned)flags)>>D_FLAG;
219 		if(shp->fdstatus[fd]&IOTTY)
220 			tty_raw(fd,1);
221 	}
222 	if(!(flags&(N_FLAG|NN_FLAG)))
223 	{
224 		Namval_t *mp;
225 		/* set up state table based on IFS */
226 		ifs = nv_getval(mp=nv_scoped(IFSNOD));
227 		if((flags&R_FLAG) && shp->ifstable['\\']==S_ESC)
228 			shp->ifstable['\\'] = 0;
229 		else if(!(flags&R_FLAG) && shp->ifstable['\\']==0)
230 			shp->ifstable['\\'] = S_ESC;
231 		shp->ifstable[delim] = S_NL;
232 		if(delim!='\n')
233 		{
234 			shp->ifstable['\n'] = 0;
235 			nv_putval(mp, ifs, NV_RDONLY);
236 		}
237 		shp->ifstable[0] = S_EOF;
238 	}
239 	sfclrerr(iop);
240 	if(np->nvfun && np->nvfun->disc->readf)
241 		return((* np->nvfun->disc->readf)(np,iop,delim,np->nvfun));
242 	was_write = (sfset(iop,SF_WRITE,0)&SF_WRITE)!=0;
243 	if(fd==0)
244 		was_share = (sfset(iop,SF_SHARE,1)&SF_SHARE)!=0;
245 	if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK)))
246 	{
247 		sh_pushcontext(&buff,1);
248 		jmpval = sigsetjmp(buff.buff,0);
249 		if(jmpval)
250 			goto done;
251 		if(timeout)
252 	                timeslot = (void*)sh_timeradd(timeout,0,timedout,(void*)iop);
253 	}
254 	if(flags&(N_FLAG|NN_FLAG))
255 	{
256 		char buf[64],*var=buf;
257 		/* reserved buffer */
258 		if((c=size)>=sizeof(buf))
259 		{
260 			if(!(var = (char*)malloc(c+1)))
261 				sh_exit(1);
262 		}
263 		if((sfset(iop,SF_SHARE,1)&SF_SHARE) && fd!=0)
264 			was_share = 1;
265 		if(size==0)
266 		{
267 			cp = sfreserve(iop,0,0);
268 			c = 0;
269 		}
270 		else
271 		{
272 			c= (shp->fdstatus[fd]&(IOTTY|IONOSEEK))?1:-1;
273 			if(flags&NN_FLAG)
274 				c = size;
275 			if(cp = sfreserve(iop,c,!(flags&NN_FLAG)))
276 				c = sfvalue(iop);
277 			else
278 				c = 0;
279 			if(c>size)
280 				c = size;
281 			if(c>0)
282 			{
283 				memcpy((void*)var,cp,c);
284 				if(flags&N_FLAG)
285 					sfread(iop,cp,c);
286 			}
287 			var[c] = 0;
288 			if(c>=size)
289 				sfclrerr(iop);
290 		}
291 		if(timeslot)
292 			timerdel(timeslot);
293 		if(nv_isattr(np,NV_BINARY))
294 		{
295 			if(c<sizeof(buf))
296 				var = memdup(var,c);
297 			nv_putval(np,var, NV_RAW);
298 			nv_setsize(np,c);
299 		}
300 		else
301 		{
302 			nv_putval(np,var,0);
303 			if(c>=sizeof(buf))
304 				free((void*)var);
305 		}
306 		goto done;
307 	}
308 	else if(cp = (unsigned char*)sfgetr(iop,delim,0))
309 		c = sfvalue(iop);
310 	else if(cp = (unsigned char*)sfgetr(iop,delim,-1))
311 		c = sfvalue(iop)+1;
312 	if(timeslot)
313 		timerdel(timeslot);
314 	if((flags&S_FLAG) && !shp->hist_ptr)
315 	{
316 		sh_histinit();
317 		if(!shp->hist_ptr)
318 			flags &= ~S_FLAG;
319 	}
320 	if(cp)
321 	{
322 		cpmax = cp + c;
323 #if SHOPT_CRNL
324 		if(delim=='\n' && c>=2 && cpmax[-2]=='\r')
325 			cpmax--;
326 #endif /* SHOPT_CRNL */
327 		if(*(cpmax-1) != delim)
328 			*(cpmax-1) = delim;
329 		if(flags&S_FLAG)
330 			sfwrite(shp->hist_ptr->histfp,(char*)cp,c);
331 		c = shp->ifstable[*cp++];
332 #if !SHOPT_MULTIBYTE
333 		if(!name && (flags&R_FLAG)) /* special case single argument */
334 		{
335 			/* skip over leading blanks */
336 			while(c==S_SPACE)
337 				c = shp->ifstable[*cp++];
338 			/* strip trailing delimiters */
339 			if(cpmax[-1] == '\n')
340 				cpmax--;
341 			if(cpmax>cp)
342 			{
343 				while((c=shp->ifstable[*--cpmax])==S_DELIM || c==S_SPACE);
344 				cpmax[1] = 0;
345 			}
346 			else
347 				*cpmax =0;
348 			if(nv_isattr(np, NV_RDONLY))
349 			{
350 				errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
351 				jmpval = 1;
352 			}
353 			else
354 				nv_putval(np,(char*)cp-1,0);
355 			goto done;
356 		}
357 #endif /* !SHOPT_MULTIBYTE */
358 	}
359 	else
360 		c = S_NL;
361 	shp->nextprompt = 2;
362 	rel= staktell();
363 	/* val==0 at the start of a field */
364 	val = 0;
365 	del = 0;
366 	while(1)
367 	{
368 		switch(c)
369 		{
370 #if SHOPT_MULTIBYTE
371 		   case S_MBYTE:
372 			if(val==0)
373 				val = (char*)(cp-1);
374 			if(sh_strchr(ifs,(char*)cp-1)>=0)
375 			{
376 				c = mbsize((char*)cp-1);
377 				if(name)
378 					cp[-1] = 0;
379 				if(c>1)
380 					cp += (c-1);
381 				c = S_DELIM;
382 			}
383 			else
384 				c = 0;
385 			continue;
386 #endif /*SHOPT_MULTIBYTE */
387 		    case S_ESC:
388 			/* process escape character */
389 			if((c = shp->ifstable[*cp++]) == S_NL)
390 				was_escape = 1;
391 			else
392 				c = 0;
393 			if(val)
394 			{
395 				stakputs(val);
396 				use_stak = 1;
397 				was_escape = 1;
398 				*val = 0;
399 			}
400 			continue;
401 
402 		    case S_EOF:
403 			/* check for end of buffer */
404 			if(val && *val)
405 			{
406 				stakputs(val);
407 				use_stak = 1;
408 			}
409 			val = 0;
410 			if(cp>=cpmax)
411 			{
412 				c = S_NL;
413 				break;
414 			}
415 			/* eliminate null bytes */
416 			c = shp->ifstable[*cp++];
417 			if(!name && val && (c==S_SPACE||c==S_DELIM||c==S_MBYTE))
418 				c = 0;
419 			continue;
420 		    case S_NL:
421 			if(was_escape)
422 			{
423 				was_escape = 0;
424 				if(cp = (unsigned char*)sfgetr(iop,delim,0))
425 					c = sfvalue(iop);
426 				else if(cp=(unsigned char*)sfgetr(iop,delim,-1))
427 					c = sfvalue(iop)+1;
428 				if(cp)
429 				{
430 					if(flags&S_FLAG)
431 						sfwrite(shp->hist_ptr->histfp,(char*)cp,c);
432 					cpmax = cp + c;
433 					c = shp->ifstable[*cp++];
434 					val=0;
435 					if(!name && (c==S_SPACE || c==S_DELIM || c==S_MBYTE))
436 						c = 0;
437 					continue;
438 				}
439 			}
440 			c = S_NL;
441 			break;
442 
443 		    case S_SPACE:
444 			/* skip over blanks */
445 			while((c=shp->ifstable[*cp++])==S_SPACE);
446 			if(!val)
447 				continue;
448 #if SHOPT_MULTIBYTE
449 			if(c==S_MBYTE)
450 			{
451 				if(sh_strchr(ifs,(char*)cp-1)>=0)
452 				{
453 					if((c = mbsize((char*)cp-1))>1)
454 						cp += (c-1);
455 					c = S_DELIM;
456 				}
457 				else
458 					c = 0;
459 			}
460 #endif /* SHOPT_MULTIBYTE */
461 			if(c!=S_DELIM)
462 				break;
463 			/* FALL THRU */
464 
465 		    case S_DELIM:
466 			if(!del)
467 				del = cp - 1;
468 			if(name)
469 			{
470 				/* skip over trailing blanks */
471 				while((c=shp->ifstable[*cp++])==S_SPACE);
472 				break;
473 			}
474 			/* FALL THRU */
475 
476 		    case 0:
477 			if(val==0 || was_escape)
478 			{
479 				val = (char*)(cp-1);
480 				was_escape = 0;
481 			}
482 			/* skip over word characters */
483 			wrd = -1;
484 			while(1)
485 			{
486 				while((c=shp->ifstable[*cp++])==0)
487 					if(!wrd)
488 						wrd = 1;
489 				if(!del&&c==S_DELIM)
490 					del = cp - 1;
491 				if(name || c==S_NL || c==S_ESC || c==S_EOF || c==S_MBYTE)
492 					break;
493 				if(wrd<0)
494 					wrd = 0;
495 			}
496 			if(wrd>0)
497 				del = (unsigned char*)"";
498 			if(c!=S_MBYTE)
499 				cp[-1] = 0;
500 			continue;
501 		}
502 		/* assign value and advance to next variable */
503 		if(!val)
504 			val = "";
505 		if(use_stak)
506 		{
507 			stakputs(val);
508 			stakputc(0);
509 			val = stakptr(rel);
510 		}
511 		if(!name && *val)
512 		{
513 			/* strip off trailing space delimiters */
514 			register unsigned char	*vp = (unsigned char*)val + strlen(val);
515 			while(shp->ifstable[*--vp]==S_SPACE);
516 			if(vp==del)
517 			{
518 				if(vp==(unsigned char*)val)
519 					vp--;
520 				else
521 					while(shp->ifstable[*--vp]==S_SPACE);
522 			}
523 			vp[1] = 0;
524 		}
525 		if(nv_isattr(np, NV_RDONLY))
526 		{
527 			errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
528 			jmpval = 1;
529 		}
530 		else
531 			nv_putval(np,val,0);
532 		val = 0;
533 		del = 0;
534 		if(use_stak)
535 		{
536 			stakseek(rel);
537 			use_stak = 0;
538 		}
539 		if(array_index)
540 		{
541 			nv_putsub(np, NIL(char*), array_index++);
542 			if(c!=S_NL)
543 				continue;
544 			name = *++names;
545 		}
546 		while(1)
547 		{
548 			if(sh_isoption(SH_ALLEXPORT)&&!strchr(nv_name(np),'.') && !nv_isattr(np,NV_EXPORT))
549 			{
550 				nv_onattr(np,NV_EXPORT);
551 				sh_envput(sh.env,np);
552 			}
553 			if(name)
554 			{
555 				nv_close(np);
556 				np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME);
557 				name = *++names;
558 			}
559 			else
560 				np = 0;
561 			if(c!=S_NL)
562 				break;
563 			if(!np)
564 				goto done;
565 			if(nv_isattr(np, NV_RDONLY))
566 			{
567 				errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
568 				jmpval = 1;
569 			}
570 			else
571 				nv_putval(np, "", 0);
572 		}
573 	}
574 done:
575 	if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK)))
576 		sh_popcontext(&buff);
577 	if(was_write)
578 		sfset(iop,SF_WRITE,1);
579 	if(!was_share)
580 		sfset(iop,SF_SHARE,0);
581 	nv_close(np);
582 	if((flags>>D_FLAG) && (shp->fdstatus[fd]&IOTTY))
583 		tty_cooked(fd);
584 	if(flags&S_FLAG)
585 		hist_flush(shp->hist_ptr);
586 	if(jmpval > 1)
587 		siglongjmp(*shp->jmplist,jmpval);
588 	return(jmpval);
589 }
590 
591