1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1982-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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 #define	SS_FLAG	0x80	/* read .csv format file */
50 
51 struct read_save
52 {
53         char	**argv;
54 	char	*prompt;
55         short	fd;
56         short	plen;
57 	int	flags;
58 	ssize_t	len;
59         long	timeout;
60 };
61 
b_read(int argc,char * argv[],Shbltin_t * context)62 int	b_read(int argc,char *argv[], Shbltin_t *context)
63 {
64 	Sfdouble_t sec;
65 	register char *name;
66 	register int r, flags=0, fd=0;
67 	register Shell_t *shp = context->shp;
68 	ssize_t	len=0;
69 	long timeout = 1000*shp->st.tmout;
70 	int save_prompt, fixargs=context->invariant;
71 	struct read_save *rp;
72 	static char default_prompt[3] = {ESC,ESC};
73 	rp = (struct read_save*)(context->data);
74 	if(argc==0)
75 	{
76 		if(rp)
77 			free((void*)rp);
78 		return(0);
79 	}
80 	if(rp)
81 	{
82 		flags = rp->flags;
83 		timeout = rp->timeout;
84 		fd = rp->fd;
85 		argv = rp->argv;
86 		name = rp->prompt;
87 		r = rp->plen;
88 		goto bypass;
89 	}
90 	while((r = optget(argv,sh_optread))) switch(r)
91 	{
92 	    case 'A':
93 		flags |= A_FLAG;
94 		break;
95 	    case 'C':
96 		flags |= C_FLAG;
97 		break;
98 	    case 't':
99 		sec = sh_strnum(opt_info.arg, (char**)0,1);
100 		timeout = sec ? 1000*sec : 1;
101 		break;
102 	    case 'd':
103 		if(opt_info.arg && *opt_info.arg!='\n')
104 		{
105 			char *cp = opt_info.arg;
106 			flags &= ~((1<<D_FLAG)-1);
107 			flags |= (mbchar(cp)<< D_FLAG);
108 		}
109 		break;
110 	    case 'p':
111 		if((fd = shp->cpipe[0])<=0)
112 			errormsg(SH_DICT,ERROR_exit(1),e_query);
113 		break;
114 	    case 'n': case 'N':
115 		flags &= ((1<<D_FLAG)-1);
116 		flags |= (r=='n'?N_FLAG:NN_FLAG);
117 		len = opt_info.num;
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 'S':
127 		flags |= SS_FLAG;
128 		break;
129 	    case 'u':
130 		fd = (int)opt_info.num;
131 		if(sh_inuse(shp,fd))
132 			fd = -1;
133 		break;
134 	    case 'v':
135 		flags |= V_FLAG;
136 		break;
137 	    case ':':
138 		errormsg(SH_DICT,2, "%s", opt_info.arg);
139 		break;
140 	    case '?':
141 		errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
142 		break;
143 	}
144 	argv += opt_info.index;
145 	if(error_info.errors)
146 		errormsg(SH_DICT,ERROR_usage(2), "%s", optusage((char*)0));
147 	if(!((r=shp->fdstatus[fd])&IOREAD)  || !(r&(IOSEEK|IONOSEEK)))
148 		r = sh_iocheckfd(shp,fd);
149 	if(fd<0 || !(r&IOREAD))
150 		errormsg(SH_DICT,ERROR_system(1),e_file+4);
151 	/* look for prompt */
152 	if((name = *argv) && (name=strchr(name,'?')) && (r&IOTTY))
153 		r = strlen(name++);
154 	else
155 		r = 0;
156 	if(argc==fixargs && (rp=newof(NIL(struct read_save*),struct read_save,1,0)))
157 	{
158 		context->data = (void*)rp;
159 		rp->fd = fd;
160 		rp->flags = flags;
161 		rp->timeout = timeout;
162 		rp->argv = argv;
163 		rp->prompt = name;
164 		rp->plen = r;
165 		rp->len = len;
166 	}
167 bypass:
168 	shp->prompt = default_prompt;
169 	if(r && (shp->prompt=(char*)sfreserve(sfstderr,r,SF_LOCKR)))
170 	{
171 		memcpy(shp->prompt,name,r);
172 		sfwrite(sfstderr,shp->prompt,r-1);
173 	}
174 	shp->timeout = 0;
175 	save_prompt = shp->nextprompt;
176 	shp->nextprompt = 0;
177 	r=sh_readline(shp,argv,fd,flags,len,timeout);
178 	shp->nextprompt = save_prompt;
179 	if(r==0 && (r=(sfeof(shp->sftable[fd])||sferror(shp->sftable[fd]))))
180 	{
181 		if(fd == shp->cpipe[0] && errno!=EINTR)
182 			sh_pclose(shp->cpipe);
183 	}
184 	return(r);
185 }
186 
187 /*
188  * here for read timeout
189  */
timedout(void * handle)190 static 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 
sh_readline(register Shell_t * shp,char ** names,volatile int fd,int flags,ssize_t size,long timeout)204 int sh_readline(register Shell_t *shp,char **names, volatile int fd, int flags,ssize_t size,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 	volatile int		keytrap;
220 	int			rel, wrd;
221 	long			array_index = 0;
222 	void			*timeslot=0;
223 	int			delim = '\n';
224 	int			jmpval=0;
225 	int			binary;
226 	int			oflags=NV_ASSIGN|NV_VARNAME;
227 	char			inquote = 0;
228 	struct	checkpt		buff;
229 	Edit_t			*ep = (struct edit*)shp->gd->ed_context;
230 	if(!(iop=shp->sftable[fd]) && !(iop=sh_iostream(shp,fd)))
231 		return(1);
232 	sh_stats(STAT_READS);
233 	if(names && (name = *names))
234 	{
235 		Namval_t *mp;
236 		if(val= strchr(name,'?'))
237 			*val = 0;
238 		if(flags&C_FLAG)
239 			oflags |= NV_ARRAY;
240 		np = nv_open(name,shp->var_tree,oflags);
241 		if(np && nv_isarray(np) && (mp=nv_opensub(np)))
242 			np = mp;
243 		if((flags&V_FLAG) && shp->gd->ed_context)
244 			((struct edit*)shp->gd->ed_context)->e_default = np;
245 		if(flags&A_FLAG)
246 		{
247 			Namarr_t *ap;
248 			flags &= ~A_FLAG;
249 			array_index = 1;
250 			if((ap=nv_arrayptr(np)) && !ap->fun)
251 				ap->nelem++;
252 			nv_unset(np);
253 			if((ap=nv_arrayptr(np)) && !ap->fun)
254 				ap->nelem--;
255 			nv_putsub(np,NIL(char*),0L);
256 		}
257 		else if(flags&C_FLAG)
258 		{
259 			char *sp =  np->nvenv;
260 			delim = -1;
261 			nv_unset(np);
262 			if(!nv_isattr(np,NV_MINIMAL))
263 				np->nvenv = sp;
264 			nv_setvtree(np);
265 		}
266 		else
267 			name = *++names;
268 		if(val)
269 			*val = '?';
270 	}
271 	else
272 	{
273 		name = 0;
274 		if(dtvnext(shp->var_tree) || shp->namespace)
275                 	np = nv_open(nv_name(REPLYNOD),shp->var_tree,0);
276 		else
277 			np = REPLYNOD;
278 	}
279 	keytrap =  ep?ep->e_keytrap:0;
280 	if(size || (flags>>D_FLAG))	/* delimiter not new-line or fixed size read */
281 	{
282 		if((shp->fdstatus[fd]&IOTTY) && !keytrap)
283 			tty_raw(fd,1);
284 		if(!(flags&(N_FLAG|NN_FLAG)))
285 		{
286 			delim = ((unsigned)flags)>>D_FLAG;
287 			ep->e_nttyparm.c_cc[VEOL] = delim;
288 			ep->e_nttyparm.c_lflag |= ISIG;
289 			tty_set(fd,TCSADRAIN,&ep->e_nttyparm);
290 		}
291 	}
292 	binary = nv_isattr(np,NV_BINARY);
293 	if(!binary && !(flags&(N_FLAG|NN_FLAG)))
294 	{
295 		Namval_t *mp;
296 		/* set up state table based on IFS */
297 		ifs = nv_getval(mp=sh_scoped(shp,IFSNOD));
298 		if((flags&R_FLAG) && shp->ifstable['\\']==S_ESC)
299 			shp->ifstable['\\'] = 0;
300 		else if(!(flags&R_FLAG) && shp->ifstable['\\']==0)
301 			shp->ifstable['\\'] = S_ESC;
302 		if(delim>0)
303 			shp->ifstable[delim] = S_NL;
304 		if(delim!='\n')
305 		{
306 			shp->ifstable['\n'] = 0;
307 			nv_putval(mp, ifs, NV_RDONLY);
308 		}
309 		shp->ifstable[0] = S_EOF;
310 		if((flags&SS_FLAG))
311 		{
312 			shp->ifstable['"'] = S_QUOTE;
313 			shp->ifstable['\r'] = S_ERR;
314 		}
315 	}
316 	sfclrerr(iop);
317 	for(nfp=np->nvfun; nfp; nfp = nfp->next)
318 	{
319 		if(nfp->disc && nfp->disc->readf)
320 		{
321 			Namval_t *mp = nv_open(name,shp->var_tree,oflags|NV_NOREF);
322 			if((c=(*nfp->disc->readf)(mp,iop,delim,nfp))>=0)
323 				return(c);
324 		}
325 	}
326 	if(binary && !(flags&(N_FLAG|NN_FLAG)))
327 	{
328 		flags |= NN_FLAG;
329 		size = nv_size(np);
330 	}
331 	was_write = (sfset(iop,SF_WRITE,0)&SF_WRITE)!=0;
332 	if(fd==0)
333 		was_share = (sfset(iop,SF_SHARE,shp->redir0!=2)&SF_SHARE)!=0;
334 	if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK)))
335 	{
336 		sh_pushcontext(shp,&buff,1);
337 		jmpval = sigsetjmp(buff.buff,0);
338 		if(jmpval)
339 			goto done;
340 		if(timeout)
341 	                timeslot = (void*)sh_timeradd(timeout,0,timedout,(void*)iop);
342 	}
343 	if(flags&(N_FLAG|NN_FLAG))
344 	{
345 		char buf[256],*var=buf,*cur,*end,*up,*v;
346 		/* reserved buffer */
347 		if((c=size)>=sizeof(buf))
348 		{
349 			if(!(var = (char*)malloc(c+1)))
350 				sh_exit(1);
351 			end = var + c;
352 		}
353 		else
354 			end = var + sizeof(buf) - 1;
355 		up = cur = var;
356 		if((sfset(iop,SF_SHARE,1)&SF_SHARE) && fd!=0)
357 			was_share = 1;
358 		if(size==0)
359 		{
360 			cp = sfreserve(iop,0,0);
361 			c = 0;
362 		}
363 		else
364 		{
365 			ssize_t	m;
366 			int	f;
367 			for (;;)
368 			{
369 				c = size;
370 				if(keytrap)
371 				{
372 					cp = 0;
373 					f = 0;
374 					m = 0;
375 					while(c-->0 && (buf[m]=ed_getchar(ep,0)))
376 						m++;
377 					if(m>0)
378 						cp = (unsigned char*)buf;
379 				}
380 				else
381 				{
382 					f = 1;
383 					if(cp = sfreserve(iop,c,SF_LOCKR))
384 						m = sfvalue(iop);
385 					else if(flags&NN_FLAG)
386 					{
387 						c = size;
388 						m = (cp = sfreserve(iop,c,0)) ? sfvalue(iop) : 0;
389 						f = 0;
390 					}
391 					else
392 					{
393 						c = sfvalue(iop);
394 						m = (cp = sfreserve(iop,c,SF_LOCKR)) ? sfvalue(iop) : 0;
395 					}
396 				}
397 				if(m>0 && (flags&N_FLAG) && !binary && (v=memchr(cp,'\n',m)))
398 				{
399 					*v++ = 0;
400 					m = v-(char*)cp;
401 				}
402 				if((c=m)>size)
403 					c = size;
404 				if(c>0)
405 				{
406 					if(c > (end-cur))
407 					{
408 						ssize_t	cx = cur - var, ux = up - var;
409 						m = (end - var) + (c - (end - cur));
410 						if (var == buf)
411 						{
412 							v = (char*)malloc(m+1);
413 							var = memcpy(v, var, cur - var);
414 						}
415 						else
416 							var = newof(var, char, m, 1);
417 						end = var + m;
418 						cur = var + cx;
419 						up = var + ux;
420 					}
421 					if(cur!=(char*)cp)
422 						memcpy((void*)cur,cp,c);
423 					if(f)
424 						sfread(iop,cp,c);
425 					cur += c;
426 #if SHOPT_MULTIBYTE
427 					if(!binary && mbwide())
428 					{
429 						int	x;
430 						int	z;
431 
432 						mbinit();
433 						*cur = 0;
434 						x = z = 0;
435 						while (up < cur && (z = mbsize(up)) > 0)
436 						{
437 							up += z;
438 							x++;
439 						}
440 						if((size -= x) > 0 && (up >= cur || z < 0) && ((flags & NN_FLAG) || z < 0 || m > c))
441 							continue;
442 					}
443 #endif
444 				}
445 #if SHOPT_MULTIBYTE
446 				if(!binary && mbwide() && (up == var || (flags & NN_FLAG) && size))
447 					cur = var;
448 #endif
449 				*cur = 0;
450 				if(c>=size || (flags&N_FLAG) || m==0)
451 				{
452 					if(m)
453 						sfclrerr(iop);
454 					break;
455 				}
456 				size -= c;
457 			}
458 		}
459 		if(timeslot)
460 			timerdel(timeslot);
461 		if(binary && !((size=nv_size(np)) && nv_isarray(np) && c!=size))
462 		{
463 			if((c==size) && np->nvalue.cp && !nv_isarray(np))
464 				memcpy((char*)np->nvalue.cp,var,c);
465 			else
466 			{
467 				Namval_t *mp;
468 				if(var==buf)
469 					var = memdup(var,c+1);
470 				nv_putval(np,var,NV_RAW);
471 				nv_setsize(np,c);
472 				if(!nv_isattr(np,NV_IMPORT|NV_EXPORT)  && (mp=(Namval_t*)np->nvenv))
473 					nv_setsize(mp,c);
474 			}
475 		}
476 		else
477 		{
478 			nv_putval(np,var,0);
479 			if(var!=buf)
480 				free((void*)var);
481 		}
482 		goto done;
483 	}
484 	else if(cp = (unsigned char*)sfgetr(iop,delim,0))
485 		c = sfvalue(iop);
486 	else if(cp = (unsigned char*)sfgetr(iop,delim,-1))
487 	{
488 		c = sfvalue(iop)+1;
489 		if(!sferror(iop) && sfgetc(iop) >=0)
490 			errormsg(SH_DICT,ERROR_exit(1),e_overlimit,"line length");
491 	}
492 	if(timeslot)
493 		timerdel(timeslot);
494 	if((flags&S_FLAG) && !shp->gd->hist_ptr)
495 	{
496 		sh_histinit((void*)shp);
497 		if(!shp->gd->hist_ptr)
498 			flags &= ~S_FLAG;
499 	}
500 	if(cp)
501 	{
502 		cpmax = cp + c;
503 #if SHOPT_CRNL
504 		if(delim=='\n' && c>=2 && cpmax[-2]=='\r')
505 			cpmax--;
506 #endif /* SHOPT_CRNL */
507 		if(*(cpmax-1) != delim)
508 			*(cpmax-1) = delim;
509 		if(flags&S_FLAG)
510 			sfwrite(shp->gd->hist_ptr->histfp,(char*)cp,c);
511 		c = shp->ifstable[*cp++];
512 #if !SHOPT_MULTIBYTE
513 		if(!name && (flags&R_FLAG)) /* special case single argument */
514 		{
515 			/* skip over leading blanks */
516 			while(c==S_SPACE)
517 				c = shp->ifstable[*cp++];
518 			/* strip trailing delimiters */
519 			if(cpmax[-1] == '\n')
520 				cpmax--;
521 			if(cpmax>cp)
522 			{
523 				while((c=shp->ifstable[*--cpmax])==S_DELIM || c==S_SPACE);
524 				cpmax[1] = 0;
525 			}
526 			else
527 				*cpmax =0;
528 			if(nv_isattr(np, NV_RDONLY))
529 			{
530 				errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
531 				jmpval = 1;
532 			}
533 			else
534 				nv_putval(np,(char*)cp-1,0);
535 			goto done;
536 		}
537 #endif /* !SHOPT_MULTIBYTE */
538 	}
539 	else
540 		c = S_NL;
541 	shp->nextprompt = 2;
542 	rel= staktell();
543 	/* val==0 at the start of a field */
544 	val = 0;
545 	del = 0;
546 	while(1)
547 	{
548 		switch(c)
549 		{
550 #if SHOPT_MULTIBYTE
551 		   case S_MBYTE:
552 			if(val==0)
553 				val = (char*)(cp-1);
554 			if(sh_strchr(ifs,(char*)cp-1)>=0)
555 			{
556 				c = mbsize((char*)cp-1);
557 				if(name)
558 					cp[-1] = 0;
559 				if(c>1)
560 					cp += (c-1);
561 				c = S_DELIM;
562 			}
563 			else
564 				c = 0;
565 			continue;
566 #endif /*SHOPT_MULTIBYTE */
567 		    case S_QUOTE:
568 			c = shp->ifstable[*cp++];
569 			inquote = !inquote;
570 			if(val)
571 			{
572 				stakputs(val);
573 				use_stak = 1;
574 				*val = 0;
575 			}
576 			continue;
577 		    case S_ESC:
578 			/* process escape character */
579 			if((c = shp->ifstable[*cp++]) == S_NL)
580 				was_escape = 1;
581 			else
582 				c = 0;
583 			if(val)
584 			{
585 				stakputs(val);
586 				use_stak = 1;
587 				was_escape = 1;
588 				*val = 0;
589 			}
590 			continue;
591 
592 		    case S_ERR:
593 			cp++;
594 			/* FALLTHROUGH */
595 		    case S_EOF:
596 			/* check for end of buffer */
597 			if(val && *val)
598 			{
599 				stakputs(val);
600 				use_stak = 1;
601 			}
602 			val = 0;
603 			if(cp>=cpmax)
604 			{
605 				c = S_NL;
606 				break;
607 			}
608 			/* eliminate null bytes */
609 			c = shp->ifstable[*cp++];
610 			if(!name && val && (c==S_SPACE||c==S_DELIM||c==S_MBYTE))
611 				c = 0;
612 			continue;
613 		    case S_NL:
614 			if(was_escape)
615 			{
616 				was_escape = 0;
617 				if(cp = (unsigned char*)sfgetr(iop,delim,0))
618 					c = sfvalue(iop);
619 				else if(cp=(unsigned char*)sfgetr(iop,delim,-1))
620 					c = sfvalue(iop)+1;
621 				if(cp)
622 				{
623 					if(flags&S_FLAG)
624 						sfwrite(shp->gd->hist_ptr->histfp,(char*)cp,c);
625 					cpmax = cp + c;
626 					c = shp->ifstable[*cp++];
627 					val=0;
628 					if(!name && (c==S_SPACE || c==S_DELIM || c==S_MBYTE))
629 						c = 0;
630 					continue;
631 				}
632 			}
633 			c = S_NL;
634 			break;
635 
636 		    case S_SPACE:
637 			/* skip over blanks */
638 			while((c=shp->ifstable[*cp++])==S_SPACE);
639 			if(!val)
640 				continue;
641 #if SHOPT_MULTIBYTE
642 			if(c==S_MBYTE)
643 			{
644 				if(sh_strchr(ifs,(char*)cp-1)>=0)
645 				{
646 					if((c = mbsize((char*)cp-1))>1)
647 						cp += (c-1);
648 					c = S_DELIM;
649 				}
650 				else
651 					c = 0;
652 			}
653 #endif /* SHOPT_MULTIBYTE */
654 			if(c!=S_DELIM)
655 				break;
656 			/* FALL THRU */
657 
658 		    case S_DELIM:
659 			if(!del)
660 				del = cp - 1;
661 			if(name)
662 			{
663 				/* skip over trailing blanks */
664 				while((c=shp->ifstable[*cp++])==S_SPACE);
665 				break;
666 			}
667 			/* FALL THRU */
668 
669 		    case 0:
670 			if(val==0 || was_escape)
671 			{
672 				val = (char*)(cp-1);
673 				was_escape = 0;
674 			}
675 			/* skip over word characters */
676 			wrd = -1;
677 			while(1)
678 			{
679 				while((c=shp->ifstable[*cp++])==0)
680 					if(!wrd)
681 						wrd = 1;
682 				if(inquote)
683 				{
684 					if(c==S_QUOTE)
685 					{
686 						if(shp->ifstable[*cp]==S_QUOTE)
687 						{
688 							if(val)
689 							{
690 								stakwrite(val,cp-(unsigned char*)val);
691 								use_stak = 1;
692 							}
693 							val = (char*)++cp;
694 						}
695 						else
696 							break;
697 					}
698 					if(c && c!=S_EOF)
699 					{
700 						if(c==S_NL)
701 						{
702 							if(val)
703 							{
704 								stakwrite(val,cp-(unsigned char*)val);
705 								use_stak=1;
706 							}
707 							if(cp = (unsigned char*)sfgetr(iop,delim,0))
708 								c = sfvalue(iop);
709 							else if(cp = (unsigned char*)sfgetr(iop,delim,-1))
710 								c = sfvalue(iop)+1;
711 							val = (char*)cp;
712 						}
713 						continue;
714 					}
715 				}
716 				if(!del&&c==S_DELIM)
717 					del = cp - 1;
718 				if(name || c==S_NL || c==S_ESC || c==S_EOF || c==S_MBYTE)
719 					break;
720 				if(wrd<0)
721 					wrd = 0;
722 			}
723 			if(wrd>0)
724 				del = (unsigned char*)"";
725 			if(c!=S_MBYTE)
726 				cp[-1] = 0;
727 			continue;
728 		}
729 		/* assign value and advance to next variable */
730 		if(!val)
731 			val = "";
732 		if(use_stak)
733 		{
734 			stakputs(val);
735 			stakputc(0);
736 			val = stakptr(rel);
737 		}
738 		if(!name && *val)
739 		{
740 			/* strip off trailing space delimiters */
741 			register unsigned char	*vp = (unsigned char*)val + strlen(val);
742 			while(shp->ifstable[*--vp]==S_SPACE);
743 			if(vp==del)
744 			{
745 				if(vp==(unsigned char*)val)
746 					vp--;
747 				else
748 					while(shp->ifstable[*--vp]==S_SPACE);
749 			}
750 			vp[1] = 0;
751 		}
752 		if(nv_isattr(np, NV_RDONLY))
753 		{
754 			errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
755 			jmpval = 1;
756 		}
757 		else
758 			nv_putval(np,val,0);
759 		val = 0;
760 		del = 0;
761 		if(use_stak)
762 		{
763 			stakseek(rel);
764 			use_stak = 0;
765 		}
766 		if(array_index)
767 		{
768 			nv_putsub(np, NIL(char*), array_index++);
769 			if(c!=S_NL)
770 				continue;
771 			name = *++names;
772 		}
773 		while(1)
774 		{
775 			if(sh_isoption(SH_ALLEXPORT)&&!strchr(nv_name(np),'.') && !nv_isattr(np,NV_EXPORT))
776 			{
777 				nv_onattr(np,NV_EXPORT);
778 				sh_envput(shp->env,np);
779 			}
780 			if(name)
781 			{
782 				nv_close(np);
783 				np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME);
784 				name = *++names;
785 			}
786 			else
787 				np = 0;
788 			if(c!=S_NL)
789 				break;
790 			if(!np)
791 				goto done;
792 			if(nv_isattr(np, NV_RDONLY))
793 			{
794 				errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
795 				jmpval = 1;
796 			}
797 			else
798 				nv_putval(np, "", 0);
799 		}
800 	}
801 done:
802 	if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK)))
803 		sh_popcontext(shp,&buff);
804 	if(was_write)
805 		sfset(iop,SF_WRITE,1);
806 	if(!was_share)
807 		sfset(iop,SF_SHARE,0);
808 	nv_close(np);
809 	if((shp->fdstatus[fd]&IOTTY) && !keytrap)
810 		tty_cooked(fd);
811 	if(flags&S_FLAG)
812 		hist_flush(shp->gd->hist_ptr);
813 	if(jmpval > 1)
814 		siglongjmp(*shp->jmplist,jmpval);
815 	return(jmpval);
816 }
817 
818