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