1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1982-2010 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	"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 	if((name = *argv) && (name=strchr(name,'?')) && (r&IOTTY))
150 		r = strlen(name++);
151 	else
152 		r = 0;
153 	if(argc==fixargs && (rp=newof(NIL(struct read_save*),struct read_save,1,0)))
154 	{
155 		((Shbltin_t*)extra)->data = (void*)rp;
156 		rp->fd = fd;
157 		rp->flags = flags;
158 		rp->timeout = timeout;
159 		rp->argv = argv;
160 		rp->prompt = name;
161 		rp->plen = r;
162 	}
163 bypass:
164 	shp->prompt = default_prompt;
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 = size;
346 				cp = sfreserve(iop,c,SF_LOCKR);
347 				f = 1;
348 				if(cp)
349 					m = sfvalue(iop);
350 				else if(flags&NN_FLAG)
351 				{
352 					c = size;
353 					m = (cp = sfreserve(iop,c,0)) ? sfvalue(iop) : 0;
354 					f = 0;
355 				}
356 				else
357 				{
358 					c = sfvalue(iop);
359 					m = (cp = sfreserve(iop,c,SF_LOCKR)) ? sfvalue(iop) : 0;
360 				}
361 				if(m>0 && (flags&N_FLAG) && !binary && (v=memchr(cp,'\n',m)))
362 				{
363 					*v++ = 0;
364 					m = v-(char*)cp;
365 				}
366 				if((c=m)>size)
367 					c = size;
368 				if(c>0)
369 				{
370 					if(c > (end-cur))
371 					{
372 						ssize_t	cx = cur - var, ux = up - var;
373 						m = (end - var) + (c - (end - cur));
374 						if (var == buf)
375 						{
376 							v = (char*)malloc(m+1);
377 							var = memcpy(v, var, cur - var);
378 						}
379 						else
380 							var = newof(var, char, m, 1);
381 						end = var + m;
382 						cur = var + cx;
383 						up = var + ux;
384 					}
385 					memcpy((void*)cur,cp,c);
386 					if(f)
387 						sfread(iop,cp,c);
388 					cur += c;
389 #if SHOPT_MULTIBYTE
390 					if(!binary && mbwide())
391 					{
392 						int	x;
393 						int	z;
394 
395 						mbinit();
396 						*cur = 0;
397 						x = z = 0;
398 						while (up < cur && (z = mbsize(up)) > 0)
399 						{
400 							up += z;
401 							x++;
402 						}
403 						if((size -= x) > 0 && (up >= cur || z < 0) && ((flags & NN_FLAG) || z < 0 || m > c))
404 							continue;
405 					}
406 #endif
407 				}
408 #if SHOPT_MULTIBYTE
409 				if(!binary && mbwide() && (up == var || (flags & NN_FLAG) && size))
410 					cur = var;
411 #endif
412 				*cur = 0;
413 				if(c>=size || (flags&N_FLAG) || m==0)
414 				{
415 					if(m)
416 						sfclrerr(iop);
417 					break;
418 				}
419 				size -= c;
420 			}
421 		}
422 		if(timeslot)
423 			timerdel(timeslot);
424 		if(binary && !((size=nv_size(np)) && nv_isarray(np) && c!=size))
425 		{
426 			if((c==size) && np->nvalue.cp && !nv_isarray(np))
427 				memcpy((char*)np->nvalue.cp,var,c);
428 			else
429 			{
430 				Namval_t *mp;
431 				if(var==buf)
432 					var = memdup(var,c+1);
433 				nv_putval(np,var,NV_RAW);
434 				nv_setsize(np,c);
435 				if(!nv_isattr(np,NV_IMPORT|NV_EXPORT)  && (mp=(Namval_t*)np->nvenv))
436 					nv_setsize(mp,c);
437 			}
438 		}
439 		else
440 		{
441 			nv_putval(np,var,0);
442 			if(var!=buf)
443 				free((void*)var);
444 		}
445 		goto done;
446 	}
447 	else if(cp = (unsigned char*)sfgetr(iop,delim,0))
448 		c = sfvalue(iop);
449 	else if(cp = (unsigned char*)sfgetr(iop,delim,-1))
450 		c = sfvalue(iop)+1;
451 	if(timeslot)
452 		timerdel(timeslot);
453 	if((flags&S_FLAG) && !shp->hist_ptr)
454 	{
455 		sh_histinit((void*)shp);
456 		if(!shp->hist_ptr)
457 			flags &= ~S_FLAG;
458 	}
459 	if(cp)
460 	{
461 		cpmax = cp + c;
462 #if SHOPT_CRNL
463 		if(delim=='\n' && c>=2 && cpmax[-2]=='\r')
464 			cpmax--;
465 #endif /* SHOPT_CRNL */
466 		if(*(cpmax-1) != delim)
467 			*(cpmax-1) = delim;
468 		if(flags&S_FLAG)
469 			sfwrite(shp->hist_ptr->histfp,(char*)cp,c);
470 		c = shp->ifstable[*cp++];
471 #if !SHOPT_MULTIBYTE
472 		if(!name && (flags&R_FLAG)) /* special case single argument */
473 		{
474 			/* skip over leading blanks */
475 			while(c==S_SPACE)
476 				c = shp->ifstable[*cp++];
477 			/* strip trailing delimiters */
478 			if(cpmax[-1] == '\n')
479 				cpmax--;
480 			if(cpmax>cp)
481 			{
482 				while((c=shp->ifstable[*--cpmax])==S_DELIM || c==S_SPACE);
483 				cpmax[1] = 0;
484 			}
485 			else
486 				*cpmax =0;
487 			if(nv_isattr(np, NV_RDONLY))
488 			{
489 				errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
490 				jmpval = 1;
491 			}
492 			else
493 				nv_putval(np,(char*)cp-1,0);
494 			goto done;
495 		}
496 #endif /* !SHOPT_MULTIBYTE */
497 	}
498 	else
499 		c = S_NL;
500 	shp->nextprompt = 2;
501 	rel= staktell();
502 	/* val==0 at the start of a field */
503 	val = 0;
504 	del = 0;
505 	while(1)
506 	{
507 		switch(c)
508 		{
509 #if SHOPT_MULTIBYTE
510 		   case S_MBYTE:
511 			if(val==0)
512 				val = (char*)(cp-1);
513 			if(sh_strchr(ifs,(char*)cp-1)>=0)
514 			{
515 				c = mbsize((char*)cp-1);
516 				if(name)
517 					cp[-1] = 0;
518 				if(c>1)
519 					cp += (c-1);
520 				c = S_DELIM;
521 			}
522 			else
523 				c = 0;
524 			continue;
525 #endif /*SHOPT_MULTIBYTE */
526 		    case S_ESC:
527 			/* process escape character */
528 			if((c = shp->ifstable[*cp++]) == S_NL)
529 				was_escape = 1;
530 			else
531 				c = 0;
532 			if(val)
533 			{
534 				stakputs(val);
535 				use_stak = 1;
536 				was_escape = 1;
537 				*val = 0;
538 			}
539 			continue;
540 
541 		    case S_EOF:
542 			/* check for end of buffer */
543 			if(val && *val)
544 			{
545 				stakputs(val);
546 				use_stak = 1;
547 			}
548 			val = 0;
549 			if(cp>=cpmax)
550 			{
551 				c = S_NL;
552 				break;
553 			}
554 			/* eliminate null bytes */
555 			c = shp->ifstable[*cp++];
556 			if(!name && val && (c==S_SPACE||c==S_DELIM||c==S_MBYTE))
557 				c = 0;
558 			continue;
559 		    case S_NL:
560 			if(was_escape)
561 			{
562 				was_escape = 0;
563 				if(cp = (unsigned char*)sfgetr(iop,delim,0))
564 					c = sfvalue(iop);
565 				else if(cp=(unsigned char*)sfgetr(iop,delim,-1))
566 					c = sfvalue(iop)+1;
567 				if(cp)
568 				{
569 					if(flags&S_FLAG)
570 						sfwrite(shp->hist_ptr->histfp,(char*)cp,c);
571 					cpmax = cp + c;
572 					c = shp->ifstable[*cp++];
573 					val=0;
574 					if(!name && (c==S_SPACE || c==S_DELIM || c==S_MBYTE))
575 						c = 0;
576 					continue;
577 				}
578 			}
579 			c = S_NL;
580 			break;
581 
582 		    case S_SPACE:
583 			/* skip over blanks */
584 			while((c=shp->ifstable[*cp++])==S_SPACE);
585 			if(!val)
586 				continue;
587 #if SHOPT_MULTIBYTE
588 			if(c==S_MBYTE)
589 			{
590 				if(sh_strchr(ifs,(char*)cp-1)>=0)
591 				{
592 					if((c = mbsize((char*)cp-1))>1)
593 						cp += (c-1);
594 					c = S_DELIM;
595 				}
596 				else
597 					c = 0;
598 			}
599 #endif /* SHOPT_MULTIBYTE */
600 			if(c!=S_DELIM)
601 				break;
602 			/* FALL THRU */
603 
604 		    case S_DELIM:
605 			if(!del)
606 				del = cp - 1;
607 			if(name)
608 			{
609 				/* skip over trailing blanks */
610 				while((c=shp->ifstable[*cp++])==S_SPACE);
611 				break;
612 			}
613 			/* FALL THRU */
614 
615 		    case 0:
616 			if(val==0 || was_escape)
617 			{
618 				val = (char*)(cp-1);
619 				was_escape = 0;
620 			}
621 			/* skip over word characters */
622 			wrd = -1;
623 			while(1)
624 			{
625 				while((c=shp->ifstable[*cp++])==0)
626 					if(!wrd)
627 						wrd = 1;
628 				if(!del&&c==S_DELIM)
629 					del = cp - 1;
630 				if(name || c==S_NL || c==S_ESC || c==S_EOF || c==S_MBYTE)
631 					break;
632 				if(wrd<0)
633 					wrd = 0;
634 			}
635 			if(wrd>0)
636 				del = (unsigned char*)"";
637 			if(c!=S_MBYTE)
638 				cp[-1] = 0;
639 			continue;
640 		}
641 		/* assign value and advance to next variable */
642 		if(!val)
643 			val = "";
644 		if(use_stak)
645 		{
646 			stakputs(val);
647 			stakputc(0);
648 			val = stakptr(rel);
649 		}
650 		if(!name && *val)
651 		{
652 			/* strip off trailing space delimiters */
653 			register unsigned char	*vp = (unsigned char*)val + strlen(val);
654 			while(shp->ifstable[*--vp]==S_SPACE);
655 			if(vp==del)
656 			{
657 				if(vp==(unsigned char*)val)
658 					vp--;
659 				else
660 					while(shp->ifstable[*--vp]==S_SPACE);
661 			}
662 			vp[1] = 0;
663 		}
664 		if(nv_isattr(np, NV_RDONLY))
665 		{
666 			errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
667 			jmpval = 1;
668 		}
669 		else
670 			nv_putval(np,val,0);
671 		val = 0;
672 		del = 0;
673 		if(use_stak)
674 		{
675 			stakseek(rel);
676 			use_stak = 0;
677 		}
678 		if(array_index)
679 		{
680 			nv_putsub(np, NIL(char*), array_index++);
681 			if(c!=S_NL)
682 				continue;
683 			name = *++names;
684 		}
685 		while(1)
686 		{
687 			if(sh_isoption(SH_ALLEXPORT)&&!strchr(nv_name(np),'.') && !nv_isattr(np,NV_EXPORT))
688 			{
689 				nv_onattr(np,NV_EXPORT);
690 				sh_envput(sh.env,np);
691 			}
692 			if(name)
693 			{
694 				nv_close(np);
695 				np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME);
696 				name = *++names;
697 			}
698 			else
699 				np = 0;
700 			if(c!=S_NL)
701 				break;
702 			if(!np)
703 				goto done;
704 			if(nv_isattr(np, NV_RDONLY))
705 			{
706 				errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np));
707 				jmpval = 1;
708 			}
709 			else
710 				nv_putval(np, "", 0);
711 		}
712 	}
713 done:
714 	if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK)))
715 		sh_popcontext(&buff);
716 	if(was_write)
717 		sfset(iop,SF_WRITE,1);
718 	if(!was_share)
719 		sfset(iop,SF_SHARE,0);
720 	nv_close(np);
721 	if((flags>>D_FLAG) && (shp->fdstatus[fd]&IOTTY))
722 		tty_cooked(fd);
723 	if(flags&S_FLAG)
724 		hist_flush(shp->hist_ptr);
725 	if(jmpval > 1)
726 		siglongjmp(*shp->jmplist,jmpval);
727 	return(jmpval);
728 }
729 
730