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
51struct 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
62int	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	}
167bypass:
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 */
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, 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		    case S_EOF:
595			/* check for end of buffer */
596			if(val && *val)
597			{
598				stakputs(val);
599				use_stak = 1;
600			}
601			val = 0;
602			if(cp>=cpmax)
603			{
604				c = S_NL;
605				break;
606			}
607			/* eliminate null bytes */
608			c = shp->ifstable[*cp++];
609			if(!name && val && (c==S_SPACE||c==S_DELIM||c==S_MBYTE))
610				c = 0;
611			continue;
612		    case S_NL:
613			if(was_escape)
614			{
615				was_escape = 0;
616				if(cp = (unsigned char*)sfgetr(iop,delim,0))
617					c = sfvalue(iop);
618				else if(cp=(unsigned char*)sfgetr(iop,delim,-1))
619					c = sfvalue(iop)+1;
620				if(cp)
621				{
622					if(flags&S_FLAG)
623						sfwrite(shp->gd->hist_ptr->histfp,(char*)cp,c);
624					cpmax = cp + c;
625					c = shp->ifstable[*cp++];
626					val=0;
627					if(!name && (c==S_SPACE || c==S_DELIM || c==S_MBYTE))
628						c = 0;
629					continue;
630				}
631			}
632			c = S_NL;
633			break;
634
635		    case S_SPACE:
636			/* skip over blanks */
637			while((c=shp->ifstable[*cp++])==S_SPACE);
638			if(!val)
639				continue;
640#if SHOPT_MULTIBYTE
641			if(c==S_MBYTE)
642			{
643				if(sh_strchr(ifs,(char*)cp-1)>=0)
644				{
645					if((c = mbsize((char*)cp-1))>1)
646						cp += (c-1);
647					c = S_DELIM;
648				}
649				else
650					c = 0;
651			}
652#endif /* SHOPT_MULTIBYTE */
653			if(c!=S_DELIM)
654				break;
655			/* FALL THRU */
656
657		    case S_DELIM:
658			if(!del)
659				del = cp - 1;
660			if(name)
661			{
662				/* skip over trailing blanks */
663				while((c=shp->ifstable[*cp++])==S_SPACE);
664				break;
665			}
666			/* FALL THRU */
667
668		    case 0:
669			if(val==0 || was_escape)
670			{
671				val = (char*)(cp-1);
672				was_escape = 0;
673			}
674			/* skip over word characters */
675			wrd = -1;
676			while(1)
677			{
678				while((c=shp->ifstable[*cp++])==0)
679					if(!wrd)
680						wrd = 1;
681				if(inquote)
682				{
683					if(c==S_QUOTE)
684					{
685						if(shp->ifstable[*cp]==S_QUOTE)
686						{
687							if(val)
688							{
689								stakwrite(val,cp-(unsigned char*)val);
690								use_stak = 1;
691							}
692							val = (char*)++cp;
693						}
694						else
695							break;
696					}
697					if(c && c!=S_EOF)
698					{
699						if(c==S_NL)
700						{
701							if(val)
702							{
703								stakwrite(val,cp-(unsigned char*)val);
704								use_stak=1;
705							}
706							if(cp = (unsigned char*)sfgetr(iop,delim,0))
707								c = sfvalue(iop);
708							else if(cp = (unsigned char*)sfgetr(iop,delim,-1))
709								c = sfvalue(iop)+1;
710							val = (char*)cp;
711						}
712						continue;
713					}
714				}
715				if(!del&&c==S_DELIM)
716					del = cp - 1;
717				if(name || c==S_NL || c==S_ESC || c==S_EOF || c==S_MBYTE)
718					break;
719				if(wrd<0)
720					wrd = 0;
721			}
722			if(wrd>0)
723				del = (unsigned char*)"";
724			if(c!=S_MBYTE)
725				cp[-1] = 0;
726			continue;
727		}
728		/* assign value and advance to next variable */
729		if(!val)
730			val = "";
731		if(use_stak)
732		{
733			stakputs(val);
734			stakputc(0);
735			val = stakptr(rel);
736		}
737		if(!name && *val)
738		{
739			/* strip off trailing space delimiters */
740			register unsigned char	*vp = (unsigned char*)val + strlen(val);
741			while(shp->ifstable[*--vp]==S_SPACE);
742			if(vp==del)
743			{
744				if(vp==(unsigned char*)val)
745					vp--;
746				else
747					while(shp->ifstable[*--vp]==S_SPACE);
748			}
749			vp[1] = 0;
750		}
751		if(nv_isattr(np, NV_RDONLY))
752		{
753			errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
754			jmpval = 1;
755		}
756		else
757			nv_putval(np,val,0);
758		val = 0;
759		del = 0;
760		if(use_stak)
761		{
762			stakseek(rel);
763			use_stak = 0;
764		}
765		if(array_index)
766		{
767			nv_putsub(np, NIL(char*), array_index++);
768			if(c!=S_NL)
769				continue;
770			name = *++names;
771		}
772		while(1)
773		{
774			if(sh_isoption(SH_ALLEXPORT)&&!strchr(nv_name(np),'.') && !nv_isattr(np,NV_EXPORT))
775			{
776				nv_onattr(np,NV_EXPORT);
777				sh_envput(shp->env,np);
778			}
779			if(name)
780			{
781				nv_close(np);
782				np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME);
783				name = *++names;
784			}
785			else
786				np = 0;
787			if(c!=S_NL)
788				break;
789			if(!np)
790				goto done;
791			if(nv_isattr(np, NV_RDONLY))
792			{
793				errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
794				jmpval = 1;
795			}
796			else
797				nv_putval(np, "", 0);
798		}
799	}
800done:
801	if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK)))
802		sh_popcontext(shp,&buff);
803	if(was_write)
804		sfset(iop,SF_WRITE,1);
805	if(!was_share)
806		sfset(iop,SF_SHARE,0);
807	nv_close(np);
808	if((shp->fdstatus[fd]&IOTTY) && !keytrap)
809		tty_cooked(fd);
810	if(flags&S_FLAG)
811		hist_flush(shp->gd->hist_ptr);
812	if(jmpval > 1)
813		siglongjmp(*shp->jmplist,jmpval);
814	return(jmpval);
815}
816
817