Home | History | Annotate | Download | only in bltins
      1 /***********************************************************************
      2 *                                                                      *
      3 *               This software is part of the ast package               *
      4 *          Copyright (c) 1982-2009 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 (at) 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 
     50 struct read_save
     51 {
     52         char	**argv;
     53 	char	*prompt;
     54         short	fd;
     55         short	plen;
     56 	int	flags;
     57         long	timeout;
     58 };
     59 
     60 int	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(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 	shp->prompt = default_prompt;
    150 	if((name = *argv) && (name=strchr(name,'?')) && (r&IOTTY))
    151 		r = strlen(name++);
    152 	else
    153 		r = 0;
    154 	if(argc==fixargs && (rp=newof(NIL(struct read_save*),struct read_save,1,0)))
    155 	{
    156 		((Shbltin_t*)extra)->data = (void*)rp;
    157 		rp->fd = fd;
    158 		rp->flags = flags;
    159 		rp->timeout = timeout;
    160 		rp->argv = argv;
    161 		rp->prompt = name;
    162 		rp->plen = r;
    163 	}
    164 bypass:
    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  */
    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 
    204 int 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 	struct	checkpt		buff;
    227 	if(!(iop=shp->sftable[fd]) && !(iop=sh_iostream(shp,fd)))
    228 		return(1);
    229 	sh_stats(STAT_READS);
    230 	if(names && (name = *names))
    231 	{
    232 		Namval_t *mp;
    233 		if(val= strchr(name,'?'))
    234 			*val = 0;
    235 		np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME);
    236 		if(np && nv_isarray(np) && (mp=nv_opensub(np)))
    237 			np = mp;
    238 		if((flags&V_FLAG) && shp->ed_context)
    239 			((struct edit*)shp->ed_context)->e_default = np;
    240 		if(flags&A_FLAG)
    241 		{
    242 			flags &= ~A_FLAG;
    243 			array_index = 1;
    244 			nv_unset(np);
    245 			nv_putsub(np,NIL(char*),0L);
    246 		}
    247 		else if(flags&C_FLAG)
    248 		{
    249 			delim = -1;
    250 			nv_unset(np);
    251 			nv_setvtree(np);
    252 		}
    253 		else
    254 			name = *++names;
    255 		if(val)
    256 			*val = '?';
    257 	}
    258 	else
    259 	{
    260 		name = 0;
    261 		if(dtvnext(shp->var_tree) || shp->namespace)
    262                 	np = nv_open(nv_name(REPLYNOD),shp->var_tree,0);
    263 		else
    264 			np = REPLYNOD;
    265 	}
    266 	if(flags>>D_FLAG)	/* delimiter not new-line or fixed size read */
    267 	{
    268 		if(flags&(N_FLAG|NN_FLAG))
    269 			size = ((unsigned)flags)>>D_FLAG;
    270 		else
    271 			delim = ((unsigned)flags)>>D_FLAG;
    272 		if(shp->fdstatus[fd]&IOTTY)
    273 			tty_raw(fd,1);
    274 	}
    275 	binary = nv_isattr(np,NV_BINARY);
    276 	if(!binary && !(flags&(N_FLAG|NN_FLAG)))
    277 	{
    278 		Namval_t *mp;
    279 		/* set up state table based on IFS */
    280 		ifs = nv_getval(mp=sh_scoped(shp,IFSNOD));
    281 		if((flags&R_FLAG) && shp->ifstable['\\']==S_ESC)
    282 			shp->ifstable['\\'] = 0;
    283 		else if(!(flags&R_FLAG) && shp->ifstable['\\']==0)
    284 			shp->ifstable['\\'] = S_ESC;
    285 		shp->ifstable[delim] = S_NL;
    286 		if(delim!='\n')
    287 		{
    288 			shp->ifstable['\n'] = 0;
    289 			nv_putval(mp, ifs, NV_RDONLY);
    290 		}
    291 		shp->ifstable[0] = S_EOF;
    292 	}
    293 	sfclrerr(iop);
    294 	for(nfp=np->nvfun; nfp; nfp = nfp->next)
    295 	{
    296 		if(nfp->disc && nfp->disc->readf)
    297 		{
    298 			if((c=(*nfp->disc->readf)(np,iop,delim,nfp))>=0)
    299 				return(c);
    300 		}
    301 	}
    302 	if(binary && !(flags&(N_FLAG|NN_FLAG)))
    303 	{
    304 		flags |= NN_FLAG;
    305 		size = nv_size(np);
    306 	}
    307 	was_write = (sfset(iop,SF_WRITE,0)&SF_WRITE)!=0;
    308 	if(fd==0)
    309 		was_share = (sfset(iop,SF_SHARE,1)&SF_SHARE)!=0;
    310 	if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK)))
    311 	{
    312 		sh_pushcontext(&buff,1);
    313 		jmpval = sigsetjmp(buff.buff,0);
    314 		if(jmpval)
    315 			goto done;
    316 		if(timeout)
    317 	                timeslot = (void*)sh_timeradd(timeout,0,timedout,(void*)iop);
    318 	}
    319 	if(flags&(N_FLAG|NN_FLAG))
    320 	{
    321 		char buf[256],*var=buf,*cur,*end,*up,*v;
    322 		/* reserved buffer */
    323 		if((c=size)>=sizeof(buf))
    324 		{
    325 			if(!(var = (char*)malloc(c+1)))
    326 				sh_exit(1);
    327 			end = var + c;
    328 		}
    329 		else
    330 			end = var + sizeof(buf) - 1;
    331 		up = cur = var;
    332 		if((sfset(iop,SF_SHARE,1)&SF_SHARE) && fd!=0)
    333 			was_share = 1;
    334 		if(size==0)
    335 		{
    336 			cp = sfreserve(iop,0,0);
    337 			c = 0;
    338 		}
    339 		else
    340 		{
    341 			ssize_t	m;
    342 			int	f;
    343 			for (;;)
    344 			{
    345 				c = (flags&NN_FLAG) ? -size : -1;
    346 				cp = sfreserve(iop,c,SF_LOCKR);
    347 				f = 1;
    348 				if(cp)
    349 					m = sfvalue(iop);
    350 				else
    351 				{
    352 					m = (cp = sfreserve(iop,size,0)) ? sfvalue(iop) : 0;
    353 					f = 0;
    354 				}
    355 				if(m>0 && (flags&N_FLAG) && !binary && (v=memchr(cp,'\n',m)))
    356 				{
    357 					*v++ = 0;
    358 					m = v-(char*)cp;
    359 				}
    360 				if((c=m)>size)
    361 					c = size;
    362 				if(c>0)
    363 				{
    364 					if(c > (end-cur))
    365 					{
    366 						ssize_t	cx = cur - var, ux = up - var;
    367 						m = (end - var) + (c - (end - cur));
    368 						if (var == buf)
    369 						{
    370 							v = (char*)malloc(m+1);
    371 							var = memcpy(v, var, cur - var);
    372 						}
    373 						else
    374 							var = newof(var, char, m, 1);
    375 						end = var + m;
    376 						cur = var + cx;
    377 						up = var + ux;
    378 					}
    379 					memcpy((void*)cur,cp,c);
    380 					if(f)
    381 						sfread(iop,cp,c);
    382 					cur += c;
    383 #if SHOPT_MULTIBYTE
    384 					if(!binary && mbwide())
    385 					{
    386 						int	x;
    387 						int	z;
    388 
    389 						mbinit();
    390 						*cur = 0;
    391 						x = z = 0;
    392 						while (up < cur && (z = mbsize(up)) > 0)
    393 						{
    394 							up += z;
    395 							x++;
    396 						}
    397 						if((size -= x) > 0 && (up >= cur || z < 0) && ((flags & NN_FLAG) || z < 0 || m > c))
    398 							continue;
    399 					}
    400 #endif
    401 				}
    402 #if SHOPT_MULTIBYTE
    403 				if(!binary && mbwide() && (up == var || (flags & NN_FLAG) && size))
    404 					cur = var;
    405 #endif
    406 				*cur = 0;
    407 				if(c>=size)
    408 					sfclrerr(iop);
    409 				break;
    410 			}
    411 		}
    412 		if(timeslot)
    413 			timerdel(timeslot);
    414 		if(binary && !((size=nv_size(np)) && nv_isarray(np) && c!=size))
    415 		{
    416 			if((c==size) && np->nvalue.cp && !nv_isarray(np))
    417 				memcpy((char*)np->nvalue.cp,var,c);
    418 			else
    419 			{
    420 				Namval_t *mp;
    421 				if(var==buf)
    422 					var = memdup(var,c);
    423 				nv_putval(np,var,NV_RAW);
    424 				nv_setsize(np,c);
    425 				if(!nv_isattr(np,NV_IMPORT|NV_EXPORT)  && (mp=(Namval_t*)np->nvenv))
    426 					nv_setsize(mp,c);
    427 			}
    428 		}
    429 		else
    430 		{
    431 			nv_putval(np,var,0);
    432 			if(var!=buf)
    433 				free((void*)var);
    434 		}
    435 		goto done;
    436 	}
    437 	else if(cp = (unsigned char*)sfgetr(iop,delim,0))
    438 		c = sfvalue(iop);
    439 	else if(cp = (unsigned char*)sfgetr(iop,delim,-1))
    440 		c = sfvalue(iop)+1;
    441 	if(timeslot)
    442 		timerdel(timeslot);
    443 	if((flags&S_FLAG) && !shp->hist_ptr)
    444 	{
    445 		sh_histinit((void*)shp);
    446 		if(!shp->hist_ptr)
    447 			flags &= ~S_FLAG;
    448 	}
    449 	if(cp)
    450 	{
    451 		cpmax = cp + c;
    452 #if SHOPT_CRNL
    453 		if(delim=='\n' && c>=2 && cpmax[-2]=='\r')
    454 			cpmax--;
    455 #endif /* SHOPT_CRNL */
    456 		if(*(cpmax-1) != delim)
    457 			*(cpmax-1) = delim;
    458 		if(flags&S_FLAG)
    459 			sfwrite(shp->hist_ptr->histfp,(char*)cp,c);
    460 		c = shp->ifstable[*cp++];
    461 #if !SHOPT_MULTIBYTE
    462 		if(!name && (flags&R_FLAG)) /* special case single argument */
    463 		{
    464 			/* skip over leading blanks */
    465 			while(c==S_SPACE)
    466 				c = shp->ifstable[*cp++];
    467 			/* strip trailing delimiters */
    468 			if(cpmax[-1] == '\n')
    469 				cpmax--;
    470 			if(cpmax>cp)
    471 			{
    472 				while((c=shp->ifstable[*--cpmax])==S_DELIM || c==S_SPACE);
    473 				cpmax[1] = 0;
    474 			}
    475 			else
    476 				*cpmax =0;
    477 			if(nv_isattr(np, NV_RDONLY))
    478 			{
    479 				errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
    480 				jmpval = 1;
    481 			}
    482 			else
    483 				nv_putval(np,(char*)cp-1,0);
    484 			goto done;
    485 		}
    486 #endif /* !SHOPT_MULTIBYTE */
    487 	}
    488 	else
    489 		c = S_NL;
    490 	shp->nextprompt = 2;
    491 	rel= staktell();
    492 	/* val==0 at the start of a field */
    493 	val = 0;
    494 	del = 0;
    495 	while(1)
    496 	{
    497 		switch(c)
    498 		{
    499 #if SHOPT_MULTIBYTE
    500 		   case S_MBYTE:
    501 			if(val==0)
    502 				val = (char*)(cp-1);
    503 			if(sh_strchr(ifs,(char*)cp-1)>=0)
    504 			{
    505 				c = mbsize((char*)cp-1);
    506 				if(name)
    507 					cp[-1] = 0;
    508 				if(c>1)
    509 					cp += (c-1);
    510 				c = S_DELIM;
    511 			}
    512 			else
    513 				c = 0;
    514 			continue;
    515 #endif /*SHOPT_MULTIBYTE */
    516 		    case S_ESC:
    517 			/* process escape character */
    518 			if((c = shp->ifstable[*cp++]) == S_NL)
    519 				was_escape = 1;
    520 			else
    521 				c = 0;
    522 			if(val)
    523 			{
    524 				stakputs(val);
    525 				use_stak = 1;
    526 				was_escape = 1;
    527 				*val = 0;
    528 			}
    529 			continue;
    530 
    531 		    case S_EOF:
    532 			/* check for end of buffer */
    533 			if(val && *val)
    534 			{
    535 				stakputs(val);
    536 				use_stak = 1;
    537 			}
    538 			val = 0;
    539 			if(cp>=cpmax)
    540 			{
    541 				c = S_NL;
    542 				break;
    543 			}
    544 			/* eliminate null bytes */
    545 			c = shp->ifstable[*cp++];
    546 			if(!name && val && (c==S_SPACE||c==S_DELIM||c==S_MBYTE))
    547 				c = 0;
    548 			continue;
    549 		    case S_NL:
    550 			if(was_escape)
    551 			{
    552 				was_escape = 0;
    553 				if(cp = (unsigned char*)sfgetr(iop,delim,0))
    554 					c = sfvalue(iop);
    555 				else if(cp=(unsigned char*)sfgetr(iop,delim,-1))
    556 					c = sfvalue(iop)+1;
    557 				if(cp)
    558 				{
    559 					if(flags&S_FLAG)
    560 						sfwrite(shp->hist_ptr->histfp,(char*)cp,c);
    561 					cpmax = cp + c;
    562 					c = shp->ifstable[*cp++];
    563 					val=0;
    564 					if(!name && (c==S_SPACE || c==S_DELIM || c==S_MBYTE))
    565 						c = 0;
    566 					continue;
    567 				}
    568 			}
    569 			c = S_NL;
    570 			break;
    571 
    572 		    case S_SPACE:
    573 			/* skip over blanks */
    574 			while((c=shp->ifstable[*cp++])==S_SPACE);
    575 			if(!val)
    576 				continue;
    577 #if SHOPT_MULTIBYTE
    578 			if(c==S_MBYTE)
    579 			{
    580 				if(sh_strchr(ifs,(char*)cp-1)>=0)
    581 				{
    582 					if((c = mbsize((char*)cp-1))>1)
    583 						cp += (c-1);
    584 					c = S_DELIM;
    585 				}
    586 				else
    587 					c = 0;
    588 			}
    589 #endif /* SHOPT_MULTIBYTE */
    590 			if(c!=S_DELIM)
    591 				break;
    592 			/* FALL THRU */
    593 
    594 		    case S_DELIM:
    595 			if(!del)
    596 				del = cp - 1;
    597 			if(name)
    598 			{
    599 				/* skip over trailing blanks */
    600 				while((c=shp->ifstable[*cp++])==S_SPACE);
    601 				break;
    602 			}
    603 			/* FALL THRU */
    604 
    605 		    case 0:
    606 			if(val==0 || was_escape)
    607 			{
    608 				val = (char*)(cp-1);
    609 				was_escape = 0;
    610 			}
    611 			/* skip over word characters */
    612 			wrd = -1;
    613 			while(1)
    614 			{
    615 				while((c=shp->ifstable[*cp++])==0)
    616 					if(!wrd)
    617 						wrd = 1;
    618 				if(!del&&c==S_DELIM)
    619 					del = cp - 1;
    620 				if(name || c==S_NL || c==S_ESC || c==S_EOF || c==S_MBYTE)
    621 					break;
    622 				if(wrd<0)
    623 					wrd = 0;
    624 			}
    625 			if(wrd>0)
    626 				del = (unsigned char*)"";
    627 			if(c!=S_MBYTE)
    628 				cp[-1] = 0;
    629 			continue;
    630 		}
    631 		/* assign value and advance to next variable */
    632 		if(!val)
    633 			val = "";
    634 		if(use_stak)
    635 		{
    636 			stakputs(val);
    637 			stakputc(0);
    638 			val = stakptr(rel);
    639 		}
    640 		if(!name && *val)
    641 		{
    642 			/* strip off trailing space delimiters */
    643 			register unsigned char	*vp = (unsigned char*)val + strlen(val);
    644 			while(shp->ifstable[*--vp]==S_SPACE);
    645 			if(vp==del)
    646 			{
    647 				if(vp==(unsigned char*)val)
    648 					vp--;
    649 				else
    650 					while(shp->ifstable[*--vp]==S_SPACE);
    651 			}
    652 			vp[1] = 0;
    653 		}
    654 		if(nv_isattr(np, NV_RDONLY))
    655 		{
    656 			errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
    657 			jmpval = 1;
    658 		}
    659 		else
    660 			nv_putval(np,val,0);
    661 		val = 0;
    662 		del = 0;
    663 		if(use_stak)
    664 		{
    665 			stakseek(rel);
    666 			use_stak = 0;
    667 		}
    668 		if(array_index)
    669 		{
    670 			nv_putsub(np, NIL(char*), array_index++);
    671 			if(c!=S_NL)
    672 				continue;
    673 			name = *++names;
    674 		}
    675 		while(1)
    676 		{
    677 			if(sh_isoption(SH_ALLEXPORT)&&!strchr(nv_name(np),'.') && !nv_isattr(np,NV_EXPORT))
    678 			{
    679 				nv_onattr(np,NV_EXPORT);
    680 				sh_envput(sh.env,np);
    681 			}
    682 			if(name)
    683 			{
    684 				nv_close(np);
    685 				np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME);
    686 				name = *++names;
    687 			}
    688 			else
    689 				np = 0;
    690 			if(c!=S_NL)
    691 				break;
    692 			if(!np)
    693 				goto done;
    694 			if(nv_isattr(np, NV_RDONLY))
    695 			{
    696 				errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
    697 				jmpval = 1;
    698 			}
    699 			else
    700 				nv_putval(np, "", 0);
    701 		}
    702 	}
    703 done:
    704 	if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK)))
    705 		sh_popcontext(&buff);
    706 	if(was_write)
    707 		sfset(iop,SF_WRITE,1);
    708 	if(!was_share)
    709 		sfset(iop,SF_SHARE,0);
    710 	nv_close(np);
    711 	if((flags>>D_FLAG) && (shp->fdstatus[fd]&IOTTY))
    712 		tty_cooked(fd);
    713 	if(flags&S_FLAG)
    714 		hist_flush(shp->hist_ptr);
    715 	if(jmpval > 1)
    716 		siglongjmp(*shp->jmplist,jmpval);
    717 	return(jmpval);
    718 }
    719 
    720