xref: /original-bsd/local/toolchest/ksh/sh/builtin.c (revision 5cf7f382)
1 /* @(#)builtin.c	1.1 */
2 
3 /*
4  *  builtin routines for the shell
5  *
6  *   David Korn
7  *   AT&T Bell Laboratories
8  *   Room 5D-112
9  *   Murray Hill, N. J. 07974
10  *   Tel. x7975
11  *
12  */
13 
14 #include	"defs.h"
15 #include	"sym.h"
16 #include	"io.h"
17 #include	"history.h"
18 #include	"builtins.h"
19 #include	"flags.h"
20 #include	"name.h"
21 #include	"mode.h"
22 #include	"brkincr.h"
23 #include	"shtype.h"
24 #include	"stak.h"
25 #ifdef JOBS
26 #include	"jobs.h"
27 #endif	/* JOBS */
28 
29 #ifdef BSD_4_2
30 #include	<sys/time.h> 	/* needed for ulimit */
31 #include	<sys/resource.h>/* needed for ulimit */
32 #define	LIM_FSIZE	RLIMIT_FSIZE
33 #define	LIM_DATA	RLIMIT_DATA
34 #define	LIM_STACK	RLIMIT_STACK
35 #define	LIM_CORE	RLIMIT_CORE
36 #define	LIM_CPU		RLIMIT_CPU
37 #define	LIM_MAXRSS	RLIMIT_RSS
38 #define	INFINITY	RLIM_INFINITY
39 #endif	/* BSD_4_2 */
40 
41 /* This module defines these routines */
42 void	builtin();
43 int	execexp();
44 
45 /* This module references these external routines */
46 extern long	aeval();
47 extern void	arg_set();
48 extern void	await();
49 extern void	cannon_path();
50 extern char	*catpath();
51 extern NAMPTR	checkfor();
52 extern FILE	*chkopen();
53 extern void	clrsig();
54 extern TREPTR	cmd();
55 extern void	do_whence();
56 extern void	done();
57 #ifdef ECHO_N
58 extern char	*echo_mode();
59 #endif	/* ECHO_N */
60 extern int	estabf();
61 extern void	execa();
62 extern void	exitsh();
63 extern void	failed();
64 extern void	fassign();
65 extern void	free();
66 extern char	*getpwd();
67 extern void	getsig();
68 extern void	gscan_all();
69 extern char	*heap();
70 extern void	hist_cancel();
71 extern void	hist_close();
72 extern histloc	hist_find();
73 extern void	hist_flush();
74 extern long	hist_list();
75 extern void	hist_open();
76 extern void	hist_subst();
77 extern long	hist_position();
78 extern void	initf();
79 extern char	*movstr();
80 extern void	oldsigs();
81 extern void	p_flush();
82 extern void	p_list();
83 extern void	p_num();
84 extern void	p_setout();
85 extern void	p_str();
86 extern void	p_time();
87 extern FILE	*pathopen();
88 extern void	pipe_close();
89 extern void	printflg();
90 extern void	prinscan();
91 extern int	printnam();
92 extern char	*realias();
93 extern char	*strcat();
94 extern char	*strchr();
95 extern char	*strcpy();
96 extern char	*substitute();
97 extern FILE	*tmp_open();
98 extern void	trace_command();
99 extern void	unassign();
100 extern char	*utos();
101 extern char	*valup();
102 
103 static int flagset();
104 static char *cmd_name;
105 static int sig_number();
106 static char *mycom[2];
107 #ifdef JOBS
108 static void sig_list();
109 #endif	/* JOBS */
110 
111 void builtin(xbuiltin, argn, com,t)
112 int	argn;
113 register char	*com[];
114 TREPTR	t;
115 {
116 	register char *a1 = com[1];
117 	register int flag = 0;
118 	struct Amemory *troot;
119 	int scoped = 0;
120 	int aflag;
121 	cmd_name = com[0];
122 	switch(xbuiltin)
123 	{
124 		case SYSEXEC:
125 			com++;
126 			ioset = 0;
127 			if(a1==0)
128 				break;
129 
130 		case SYSLOGIN:
131 
132 			if(is_option(RSHFLG))
133 				failed(cmd_name,restricted);
134 			else
135 			{
136 #ifdef JOBS
137 				if(close_jobs() < 0)
138 				{
139 					exitval=1;
140 					break;
141 				}
142 #endif /* JOBS */
143 				/* force bad exec to terminate shell */
144 				states &= ~(TTYFLG|BUILTIN);
145 				oldsigs();
146 				hist_close();
147 				rmtemp(0);
148 				execa(com,(ARGPTR)NULL);
149 				done(0);
150 			}
151 
152 		case SYSTEST:	/* test	expression */
153 			exitval = testfn(argn, com);
154 			break;
155 
156 
157 		case SYSPWD:	/* pwd routine */
158 			argn = 1;
159 			if((*mycom = getpwd(1))==NULL)
160 				failed(cmd_name,pwderr);
161 			com = mycom-1;
162 
163 		case SYSECHO:	/* system V echo routine */
164 			/* equivalent to print - */
165 			com--;
166 			argn++;
167 #ifdef ECHO_N
168 			/* This mess is because /bin/echo on BSD is archaic */
169 			a1 = echo_mode();
170 #else
171 			a1 = minus;
172 #endif	/* ECHO_N */
173 
174 		case SYSPRINT:	/* print routine */
175 		{
176 			register FILE *fd;
177 			int raw = 0;
178 			wdnum =  1;
179 			while(a1 && *a1 == '-')
180 			{
181 				int c = *(a1+1);
182 				/* handle the -R flag for BSD style echo */
183 				if(flag&R_JUST)
184 				{
185 					if(strcmp(a1,"-n")==0)
186 						c = 0;
187 					else
188 						break;
189 				}
190 				flag |= flagset(a1,~(N_FLAG|R_FLAG|P_FLAG|U_FLAG|S_FLAG|R_JUST));
191 				com++;
192 				argn--;
193 				if(c==0)
194 					break;
195 				a1 = com[1];
196 			}
197 			wdnum %= 10;
198 			if(flag&(R_FLAG|R_JUST))
199 				raw = 1;
200 			if(flag&S_FLAG)
201 			{
202 				/* print to history file */
203 				hist_open();
204 				if(fc_fix==NULL)
205 					failed(cmd_name,nohistory);
206 				fd = fc_fix->fixfd;
207 				states |= FIXFLG;
208 				goto skip;
209 			}
210 			else if(flag&P_FLAG)
211 			{
212 				if((fd=cpipe[OTPIPE])==NULL)
213 					failed(cmd_name,noquery);
214 			}
215 			else if(flag&U_FLAG)
216 			{
217 				fd = file_fd(wdnum);
218 				if(!fiswrite(fd))
219 					failed(cmd_name,badfile);
220 			}
221 			else
222 				fd = standout;
223 
224 			clearerr(fd);
225 			p_setout(fd);
226 		skip:
227 			if(echo_list(raw,com+1,fd) && (flag&N_FLAG)==0)
228 				putc(NL,fd);
229 			if(flag&S_FLAG)
230 				hist_flush();
231 			break;
232 		}
233 
234 		case SYSLET:
235 		{
236 			if(argn < 2)
237 				failed(cmd_name,argcount);
238 			while(--argn)
239 				exitval = !aeval(*++com);
240 			break;
241 		}
242 
243 		/*
244 		 * The following few builtins are provided to set,print,
245 		 * and test attributes and variables for shell variables,
246 		 * aliases, and functions.
247 		 * In addition, typeset -f can be used to test whether a
248 		 * function has been defined or to list all defined functions
249 		 * Note readonly is same as typeset -r.
250 		 * Note export is same as typeset -x.
251 		 */
252 		case SYSRDONLY:
253 			flag = R_FLAG;
254 			aflag = '-';
255 			goto typset;
256 
257 		case SYSXPORT:
258 			flag = X_FLAG;
259 			aflag = '-';
260 			goto typset;
261 
262 		case SYSALIAS:
263 		case SYSTYPESET:
264 		{
265 			FILE *fd;
266 			int type;	/* 0 for typeset, non-zero for alias */
267 			flag = 0;
268 			aflag = (a1?*a1:0);
269 			wdnum = 0;
270 			if(aflag == '-' || aflag == '+')
271 			{
272 				flag = flagset(a1,~(L_JUST|R_JUST|Z_FILL|INT_GER|L_TO_U
273 					|U_TO_L|X_FLAG|R_FLAG|F_FLAG|P_FLAG|T_FLAG
274 					|A_FLAG));
275 				com++;
276 			}
277 			if((flag&INT_GER) && (flag&(L_JUST|R_JUST|Z_FILL)))
278 				failed(cmd_name,badopt);
279 			/* S_FLAG forces name to be in newest scope */
280 			if(fn_depth)
281 				scoped = S_FLAG;
282 
283 		typset:
284 			type = 0;
285 			if(xbuiltin == SYSALIAS)
286 			{
287 				if(flag&~(N_EXPORT|T_FLAG))
288 					failed(cmd_name,badopt);
289 				troot = alias;
290 				/* setname treats this value specially */
291 				type = V_FLAG;
292 			}
293 			else if(flag&F_FLAG)
294 			{
295 				if(flag&~(N_EXPORT|F_FLAG|T_FLAG))
296 					failed(cmd_name,badopt);
297 				troot = prnames;
298 				flag &= ~F_FLAG;
299 			}
300 			else
301 				troot = namep;
302 			if(flag&P_FLAG)
303 			{
304 				flag &= ~P_FLAG;
305 				if((fd=cpipe[OTPIPE])==NULL)
306 					failed(cmd_name,noquery);
307 			}
308 			else
309 				fd = standout;
310 			p_setout(fd);
311 			if(aflag == 0)
312 			{
313 				if(type)
314 					prinscan(fd,0,troot,0);
315 				else
316 					gscan_all(printflg,troot);
317 				break;
318 			}
319 			if(com[1])
320 			{
321 				while(a1 = *++com)
322 				{
323 					register unsigned newflag;
324 					register struct Namnod *np;
325 					struct Namnod  *setname();
326 					unsigned curflag;
327 					if(troot == prnames)
328 					{
329 						/*
330 						 *functions can be exported or
331 						 * traced but not set
332 						 */
333 						if(np=checkfor(a1,prnames))
334 						{
335 							if((flag&(N_EXPORT|T_FLAG))==0)
336 							{
337 								printnam(np,0);
338 								continue;
339 							}
340 							if(aflag=='-')
341 								attrib(np,flag);
342 							else if(aflag=='+')
343 								pattrib(np,~flag);
344 						}
345 						else
346 							exitval++;
347 						continue;
348 					}
349 					np = setname (a1,(type|scoped));
350 					/* tracked alias */
351 					if(type && (flag&T_FLAG) && aflag=='-')
352 					{
353 						attrib(np,flag|N_EXPORT);
354 						realias(np);
355 						continue;
356 					}
357 					if(flag==0 && aflag!='-' && strchr(a1,'=') == NULL)
358 					{
359 						/* type==0 for TYPESET */
360 						if(type==0)
361 						{
362 							if(valup(np))
363 								printflg(np);
364 							else
365 								exitval++;
366 							continue;
367 						}
368 						if(printnam(np,0) == 0)
369 						{
370 							fputs(a1,fd);
371 							p_str(noalias,NL);
372 							exitval++;
373 						}
374 						continue;
375 					}
376 					curflag = namflag(np);
377 					if (aflag == '-')
378 					{
379 						newflag = curflag | flag;
380 						if (flag & INT_GER)
381 							newflag &= ~(R_JUST|L_JUST);
382 						else if (flag & (L_JUST|R_JUST))
383 						{
384 							newflag &= ~INT_GER;
385 							if (flag & L_JUST)
386 								newflag &= ~R_JUST;
387 							else
388 								newflag &= ~L_JUST;
389 						}
390 						if (flag & U_TO_L)
391 							newflag &= ~L_TO_U;
392 						else if (flag & L_TO_U)
393 							newflag &= ~U_TO_L;
394 					}
395 					else
396 						newflag = curflag & ~flag;
397 					if (aflag && (wdnum>0 || (curflag!=newflag)))
398 					{
399 #ifdef apollo
400 						/* keep aliases from going
401 						   into environment */
402 						if(type)
403 							namflag(np) = newflag;
404 						else
405 							chattrib (np, newflag,wdnum);
406 #endif /* apollo */
407 						chattrib (np, newflag,wdnum);
408 					}
409 				}
410 			}
411 			else
412 				prinscan(fd,flag,troot,aflag=='+');
413 			break;
414 		}
415 
416 
417 		/*
418 		 * The removing of Shell variable names, aliases, and functions
419 		 * is performed here.
420 		 * Unset functions with unset -f
421 		 * Non-existent items being deleted give non-zero exit status
422 		 */
423 		case SYSUNALIAS:
424 		case SYSUNSET:
425 		{
426 			register NAMPTR np;
427 #ifdef apollo
428 			short namlen;
429 #endif /* apollo */
430 			if(xbuiltin == SYSUNALIAS)
431 			{
432 				troot = alias;
433 				goto unall;
434 			}
435 			if(a1 && *a1 == '-')
436 			{
437 				flag = flagset(a1,~F_FLAG);
438 				com++;
439 				argn--;
440 				troot = prnames;
441 			}
442 			else
443 				troot = namep;
444 		unall:
445 			if(argn < 2)
446 				failed(cmd_name,argcount);
447 			while(--argn)
448 			{
449 				a1 = *++com;
450 				np=checkfor(a1,troot);
451 				if(np)
452 				{
453 					if(troot==namep)
454 					{
455 						if(attest(np,ARRAY) && (
456 							(a1=strchr(a1,']'))==NIL
457 							|| astchar(a1[-1])))
458 							arayp(np)->adot = NO_SUBSCRIPT;
459 						if (attest (np, N_RDONLY))
460 							failed(np->namid,wtfailed);
461 #ifdef apollo
462 						namlen =strlen(np->namid);
463 						ev_$delete_var(np->namid,&namlen);
464 #endif /* apollo */
465 					}
466 					unassign(np);
467 				}
468 				else
469 					exitval = 1;
470 			}
471 			break;
472 		}
473 
474 		case SYSDOT:
475 			if(a1)
476 			{
477 				register FILE *f;
478 				if((f=pathopen(a1)) == NULL)
479 					failed(a1,notfound);
480 				else
481 				{
482 					if(argn > 2)
483 						arg_set(com+1);
484 					execexp((char*)0,f);
485 				}
486 			}
487 			break;
488 
489 		case SYSTIMES:
490 		{
491 			long int t[4];
492 			times(t);
493 			p_setout(standout);
494 			p_time(t[0],' ');
495 			p_time(t[1],NL);
496 			p_time(t[2],' ');
497 			p_time(t[3],NL);
498 			break;
499 		}
500 
501 		case SYSRETURN:	/* return from a subroutine */
502 			if(freturn)
503 			{
504 				exitval = (a1?atoi(a1):oldexit);
505 				longjmp(*freturn,1);
506 			}
507 
508 		case SYSEXIT:
509 #ifdef JOBS
510 			if(close_jobs()<0)
511 				break;
512 #endif
513 			states &= ~(TTYFLG|BUILTIN);	/* force exit */
514 			exitsh(a1?atoi(a1):oldexit);
515 
516 		case SYSNULL:
517 			break;
518 
519 		case SYSCONT:
520 			if(loopcnt)
521 			{
522 				execbrk = breakcnt = 1;
523 				if(a1)
524 					breakcnt = atoi(a1);
525 				if(breakcnt > loopcnt)
526 					breakcnt = loopcnt;
527 				else
528 					breakcnt = -breakcnt;
529 			}
530 			break;
531 
532 		case SYSBREAK:
533 			if(loopcnt)
534 			{
535 				execbrk = breakcnt = 1;
536 				if(a1)
537 					breakcnt = atoi(a1);
538 				if(breakcnt > loopcnt)
539 					breakcnt = loopcnt;
540 			}
541 			break;
542 
543 		case SYSTRAP:
544 			if(a1)
545 			{
546 				register BOOL	clear;
547 				char *action = a1;
548 				if((clear=isdigit(*a1))==0)
549 				{
550 					++com;
551 					if(*a1=='-')
552 						clear++;
553 				}
554 				while(a1 = *++com)
555 				{
556 					flag = sig_number(a1);
557 					if(flag>MAXTRAP || flag<MINTRAP)
558 						failed(a1,badtrap);
559 					else if(clear)
560 						clrsig(flag);
561 					else
562 					{
563 						free(trapcom[flag]);
564 						trapcom[flag] = heap(action);
565 						if(*action)
566 							getsig(flag);
567 						else
568 							ignsig(flag);
569 					}
570 				}
571 			}
572 			else /* print out current traps */
573 			{
574 				p_setout(standout);
575 				for(flag=0; flag<=MAXTRAP; flag++)
576 					if(trapcom[flag])
577 					{
578 						p_num(flag,':');
579 						p_str(trapcom[flag],NL);
580 					}
581 			}
582 			break;
583 
584 		case SYSCD:
585 		{
586 			register char *dp;
587 			register char *cdpath = nullstr;
588 			char newdir[256];	/* enough for any pathname */
589 			char *oldpwd;
590 			if(is_option(RSHFLG))
591 				failed(cmd_name,restricted);
592 			else if(argn >3)
593 				failed(cmd_name, argcount);
594 			if(argn==3)
595 				a1 = substitute(getpwd(0),a1,com[2],newdir);
596 			else if(a1==0 || *a1==0)
597 				a1 = valup(HOME);
598 			else if(*a1 == '-' && *(a1+1) == 0)
599 				a1 = valup(OLDPWDNOD);
600 			if(a1==0 || *a1==0)
601 				failed(cmd_name,argn==3?badsub:baddir);
602 			if(*a1 != '/')
603 				cdpath = valup(CDPNOD);
604 			if(cdpath==0)
605 				cdpath = nullstr;
606 			if(*a1=='.')
607 			{
608 				/* test for pathname . ./ .. or ../ */
609 				if(*(dp=a1+1) == '.')
610 					dp++;
611 				if(*dp==0 || *dp=='/')
612 					cdpath = nullstr;
613 			}
614 			do
615 			{
616 				dp = cdpath;
617 				cdpath=catpath(dp,a1);
618 			}
619 			while((flag=chdir(curstak()))<0 && cdpath);
620 			if(flag<0)
621 				failed(a1,baddir);
622 			if(a1 == valup(OLDPWDNOD) || argn==3)
623 				dp = a1;	/* print out directory for cd - */
624 			a1 = (char*)fixstak();
625 			if(*dp && *dp!= ':' && (states&PROMPT) && strchr(a1,'/'))
626 			{
627 				p_setout(standout);
628 				p_str(a1,NL);
629 			}
630 			oldpwd = getpwd(0);
631 			fassign(OLDPWDNOD,oldpwd);
632 			if(*a1 == '/')
633 				strcpy(newdir,a1);
634 			else
635 			{
636 				dp = movstr(oldpwd,newdir);
637 				*dp++ = '/';
638 				movstr(a1,dp);
639 			}
640 			/* eliminate redundant / */
641 			a1 = newdir;
642 			cannon_path(a1);
643 			fassign(PWDNOD,a1);
644 #ifndef INT16
645 			/* Because of possible symbolic links, make sure we are where
646 			 * we think we are.
647 			 */
648 			if(!eq_inode(dot,a1))
649 				chdir(a1);
650 #endif	/* INT16 */
651 			break;
652 		}
653 
654 		case SYSSHFT:
655 		{
656 			flag = (a1?aeval(a1):1);
657 			if(flag<0 || dolc<flag)
658 				failed(cmd_name,badnum);
659 			else
660 			{
661 				dolv += flag;
662 				dolc -= flag;
663 			}
664 			break;
665 		}
666 
667 		case SYSWAIT:
668 			await(a1?atoi(a1):-1,1);
669 			break;
670 
671 		case SYSREAD:
672 		{
673 			register FILE *fd;
674 			wdnum = 0;
675 			while(a1 && *a1 == '-')
676 			{
677 				flag |= flagset(a1,~(R_FLAG|P_FLAG|U_FLAG|S_FLAG));
678 				com++;
679 				argn--;
680 				if(*(a1+1)==0)
681 					break;
682 				a1 = com[1];
683 			}
684 			if(flag&P_FLAG)
685 			{
686 				fd = cpipe[INPIPE];
687 				states |= PROMPT;
688 			}
689 			else if(flag&U_FLAG)
690 				fd = file_fd(wdnum);
691 			else
692 				fd = stdin;
693 			if(!fisread(fd))
694 				failed(cmd_name,badfile);
695 			if(isatty(fileno(fd)))
696 			{
697 				FILE *fdo;
698 				p_setout(output);
699 				states |= PROMPT;
700 				/* look for prompt */
701 				if(a1=strchr(a1,'?'))
702 				{
703 					if(fiswrite(fd)==0 ||
704 					(fdo=fdopen(dup(fileno(fd)),"w"))==NULL)
705 						fdo = stderr;
706 					p_setout(fdo);
707 					fputs(a1+1,fdo);
708 					if(fdo!=stderr)
709 						fclose(fdo);
710 				}
711 			}
712 			readvar(&com[1],fd,flag&(R_FLAG|S_FLAG));
713 			if(feof(fd))
714 			{
715 				exitval=1;
716 				if(flag&P_FLAG)
717 				{
718 					pipe_close(cpipe);
719 					cpipe[INPIPE]=0;
720 					cpid = 0;
721 				}
722 			}
723 			clearerr(fd);
724 			break;
725 		}
726 
727 		case SYSSET:
728 			flag = is_option(EXECPR);
729 			if(a1)
730 			{
731 				register int argc;
732 				argc = arg_opts(argn,com);
733 				/* RWAIT is set if -- flag is given */
734 				if(argc>1 || (states&RWAIT))
735 					arg_set(com+argn-argc);
736 				states &= ~(RWAIT|READPR|MONITOR);
737 				states |= is_option(READPR|MONITOR);
738 			}
739 			if(flag)
740 				trace_command(com);
741 			if(a1==0 && ((COMPTR) t)->comset==0)
742 				/*scan name chain and print*/
743 				prinscan(standout,0,namep,0);
744 			break;
745 
746 		case SYSEVAL:
747 			if(a1)
748 				execexp(a1,(FILE*)&com[2]);
749 			break;
750 
751 		case SYSFC:
752 		{
753 			register struct fixcmd *fp;
754 			FILE *fdo;
755 			char *argv[2];
756 			char fname[TMPSIZ];
757 			int index2;
758 			int indx = -1; /* used as subscript for range */
759 			char *edit = NULL;		/* name of editor */
760 			char *replace = NULL;		/* replace old=new */
761 			int incr;
762 			int range[2];	/* upper and lower range of commands */
763 			int lflag = 0;
764 			int nflag = 0;
765 			int rflag = 0;
766 			histloc location;
767 			wdnum = 0;
768 			hist_open();
769 			if((fp=fc_fix)==NULL)
770 				failed(cmd_name,nohistory);
771 			while((a1=com[1]) && *a1 == '-')
772 			{
773 				flag = flagset(a1,~(E_FLAG|L_FLAG|N_FLAG|R_FLAG));
774 				if(flag==0)
775 				{
776 					range[++indx] = fp->fixind - wdnum-1;
777 					wdnum = 0;
778 					if(indx==1)
779 						break;
780 				}
781 				else
782 				{
783 					if(flag&E_FLAG)
784 					{
785 						/* name of editor specified */
786 						com++;
787 						if((edit=com[1]) == NULL)
788 							failed(cmd_name,argexp);
789 					}
790 					if(flag&N_FLAG)
791 						nflag++;
792 					if(flag&L_FLAG)
793 						lflag++;
794 					if(flag&R_FLAG)
795 						rflag++;
796 				}
797 				com++;
798 			}
799 			flag = indx;
800 			while(flag<1 && (a1=com[1]))
801 			{
802 				if(isdigit(*a1) || *a1 == '-')
803 				{
804 					/* see if completely numeric */
805 					do	a1++;
806 					while(isdigit(*a1));
807 					if(*a1==0)
808 					{
809 						a1 = com[1];
810 						range[++flag] = atoi(a1);
811 						if(*a1 == '-')
812 							range[flag] += (fp->fixind-1);
813 						com++;
814 						continue;
815 					}
816 				}
817 				/* look for old=new argument */
818 				else if(replace==NULL && strchr(a1+1,'='))
819 				{
820 					replace = a1;
821 					com++;
822 					continue;
823 				}
824 				/* search for last line starting with string */
825 				location = hist_find(com[1],fp->fixind-1,0,-1);
826 				if((range[++flag] = location.his_command) < 0)
827 					failed(com[1],notfound);
828 				com++;
829 			}
830 			if(flag <0)
831 			{
832 				/* set default starting range */
833 				if(lflag)
834 				{
835 					flag = fp->fixind-16;
836 					if(flag<1)
837 						flag = 1;
838 				}
839 				else
840 					flag = fp->fixind-2;
841 				range[0] = flag;
842 				flag = 0;
843 			}
844 			if(flag==0)
845 				/* set default termination range */
846 				range[1] = (lflag?fp->fixind-1:range[0]);
847 			if((index2 = fp->fixind - fp->fixmax) <=0)
848 			index2 = 1;
849 			/* check for valid ranges */
850 			for(flag=0;flag<2;flag++)
851 				if(range[flag]<index2 ||
852 					range[flag]>=(fp->fixind-(lflag==0)))
853 					failed(cmd_name,badnum);
854 			if(edit && *edit=='-' && range[0]!=range[1])
855 				failed(cmd_name,badnum);
856 			/* now list commands from range[rflag] to range[1-rflag] */
857 			incr = 1;
858 			flag = rflag>0;
859 			if(range[1-flag] < range[flag])
860 				incr = -1;
861 			if(lflag)
862 			{
863 				fdo = standout;
864 				a1 = "\n\t";
865 			}
866 			else
867 			{
868 				fdo = tmp_open(fname);
869 				a1 = "\n";
870 				nflag++;
871 			}
872 			p_setout(fdo);
873 			while(1)
874 			{
875 				if(nflag==0)
876 					p_num(range[flag],'\t');
877 				else if(lflag)
878 					putc('\t',fdo);
879 				hist_list(hist_position(range[flag]),EOF,a1);
880 				if(lflag && (trapnote&SIGSET))
881 					exitsh(SIGFAIL);
882 				if(range[flag] == range[1-flag])
883 					break;
884 				range[flag] += incr;
885 			}
886 			fseek(fp->fixfd,0L,2);
887 			if(lflag)
888 				return;
889 			p_setout(stderr);
890 			a1 = edit;
891 			if(a1==NULL && (a1=valup(FCEDNOD)) == NULL)
892 				a1 = defedit;
893 			if(*a1 != '-')
894 			{
895 				argv[0] =  fname;
896 				argv[1] = NULL;
897 				execexp(a1,(FILE*)argv);
898 			}
899 			closefd(fdo);
900 			fdo = chkopen(fname);
901 			unlink(fname);
902 			/* don't history fc itself */
903 			hist_cancel();
904 			states |= (READPR|FIXFLG);	/* echo lines as read */
905 			if(replace!=NULL)
906 				hist_subst(cmd_name,fdo,replace);
907 			else if(exitval == 0)
908 				 execexp((char*)0,fdo);
909 			else
910 			{
911 				fclose(fdo);
912 				if(is_option(READPR)==0)
913 					states &= ~(READPR|FIXFLG);
914 			}
915 			break;
916 		}
917 
918 		case SYSWHENCE:
919 		{
920 			if(a1 && *a1 == '-')
921 			{
922 				flag = flagset(a1,~V_FLAG);
923 				com++;
924 				argn--;
925 			}
926 			if(argn < 2)
927 				failed(cmd_name,argcount);
928 			p_setout(standout);
929 			do_whence(com,flag);
930 			break;
931 		}
932 
933 
934 		case SYSUMASK:
935 		{
936 			if(a1)
937 			{
938 				register int c;
939 				flag = 0;
940 				while(c = *a1++)
941 				{
942 					if (c>='0' && c<='7')
943 						flag = (flag<<3) + (c-'0');
944 					else
945 						failed(cmd_name,badnum);
946 				}
947 				umask(flag);
948 			}
949 			else
950 			{
951 				p_setout(standout);
952 #ifdef pdp11
953 				a1 = utos((long)(argn=umask(0)),8);
954 #else
955 				a1 = utos((unsigned long)(argn=umask(0)),8);
956 #endif /* pdp11 */
957 				umask(argn);
958 				*++a1 = '0';
959 				p_str(a1,NL);
960 			}
961 			break;
962 		}
963 
964 #ifndef apollo
965 
966 #ifdef BSD
967 #define BLK_SIZ		512
968 #define KBYTE		1024
969 #ifdef BSD_4_2
970 #else
971 #include	<sys/vlimit.h>
972 #endif
973 		case SYSULIMIT:
974 		{
975 #ifdef BSD_4_2
976 			struct rlimit rlp;
977 #endif
978 			long i;
979 			char *opts = 0;
980 			char label=0;
981 			int n;
982 			int unit = BLK_SIZ;
983 			flag = LIM_FSIZE;
984 			if(a1 && *a1 == '-')
985 				opts = ++a1;
986 			do
987 			{
988 				if(opts)
989 				{
990 					switch(*opts)
991 					{
992 						case 'a':
993 							label++;
994 							opts = "tmdsfc";
995 						case 't':
996 							flag = LIM_CPU;
997 							unit = 1;
998 							n = 0;
999 							break;
1000 						case 'c':
1001 							flag = LIM_CORE;
1002 							n = 5;
1003 							break;
1004 						case 'f':
1005 							flag = LIM_FSIZE;
1006 							n = 4;
1007 							break;
1008 						case 'd':
1009 							flag = LIM_DATA;
1010 							unit = KBYTE;
1011 							n = 2;
1012 							break;
1013 						case 's':
1014 							flag = LIM_STACK;
1015 							unit = KBYTE;
1016 							n = 3;
1017 							break;
1018 						case 'm':
1019 							flag = LIM_MAXRSS;
1020 							unit = KBYTE;
1021 							n = 1;
1022 							break;
1023 						default:
1024 							failed(cmd_name,badopt);
1025 					}
1026 					if((a1 && *++a1)||(label&&com[2]))
1027 						failed(cmd_name,badopt);
1028 					a1 = com[2];
1029 				}
1030 				if(a1)
1031 				{
1032 					if((i=aeval(a1)) < 0)
1033 						failed(cmd_name,badnum);
1034 					i *= unit;
1035 #ifdef 	BSD_4_2
1036 					if(getrlimit(flag,&rlp) <0)
1037 						failed(cmd_name,badnum);
1038 					rlp.rlim_cur = i;
1039 					if(setrlimit(flag,&rlp) <0)
1040 						failed(cmd_name,badnum);
1041 #endif
1042 				}
1043 				else
1044 				{
1045 #ifdef 	BSD_4_2
1046 				if(getrlimit(flag,&rlp) <0)
1047 						failed(cmd_name,badnum);
1048 					i = rlp.rlim_cur;
1049 #else
1050 					i = -1;
1051 				}
1052 				if((i=vlimit(flag,i)) < 0)
1053 					failed(cmd_name,badnum);
1054 				if(a1==0)
1055 				{
1056 #endif
1057 					p_setout(standout);
1058 					if(label)
1059 						p_str(limit_names[n],SP);
1060 					if(i!=INFINITY)
1061 					{
1062 						i = (i+unit-1)/unit;
1063 						p_str(utos((unsigned long)i,10),NL);
1064 					}
1065 					else
1066 						p_str(unlimited,NL);
1067 				}
1068 			}
1069 			while(opts && *++opts);
1070 			break;
1071 		}
1072 #else
1073 		case SYSULIMIT:
1074 		{
1075 #ifndef VENIX
1076 			long i;
1077 			long ulimit();
1078 			register int mode = 2;
1079 			if(a1 && *a1 == '-')
1080 			{
1081 #ifdef RT
1082 				flag = flagset(a1,~(F_FLAG|P_FLAG));
1083 #else
1084 				flag = flagset(a1,~F_FLAG);
1085 #endif /* RT */
1086 				a1 = com[2];
1087 			}
1088 			if(flag&P_FLAG)
1089 				mode = 5;
1090 			if(a1)
1091 			{
1092 				if((i=aeval(a1)) < 0)
1093 					failed(cmd_name,badnum);
1094 			}
1095 			else
1096 			{
1097 				mode--;
1098 				i = -1;
1099 			}
1100 			if((i=ulimit(mode,i)) < 0)
1101 				failed(cmd_name,badnum);
1102 			if(a1==0)
1103 			{
1104 				p_setout(standout);
1105 # ifdef pdp11
1106 				p_str(utos((long)i,10),NL);
1107 # else
1108 				p_str(utos((unsigned long)i,10),NL);
1109 # endif /* pdp11 */
1110 			}
1111 #endif	/* VENIX */
1112 			break;
1113 		}
1114 #endif
1115 #endif	/* apollo */
1116 
1117 #ifdef JOBS
1118 # if BSD || SXT
1119 		case SYSBG:
1120 			flag = 1;
1121 		case SYSFG:
1122 			if((states&MONITOR)==0)
1123 			{
1124 				exitval = 1;
1125 				if(states&PROMPT)
1126 					failed(cmd_name,j_no_jctl);
1127 				break;
1128 			}
1129 			if(argn==1)
1130 				a1 = nullstr;
1131 			if(switch_jobs(a1,flag)==0)
1132 				failed(cmd_name,j_no_job);
1133 			break;
1134 # endif
1135 
1136 		case SYSJOBS:
1137 			if(a1 && *a1 == '-')
1138 				flag = flagset(a1,~L_FLAG);
1139 			list_jobs(flag);
1140 			break;
1141 
1142 		case SYSKILL:
1143 		{
1144 			if(argn < 2)
1145 				failed(cmd_name,argcount);
1146 			/* just in case we send a kill -9 $$ */
1147 			p_flush();
1148 			flag = 15;
1149 			if(*a1 == '-')
1150 			{
1151 				a1++;
1152 				if(*a1 == 'l')
1153 				{
1154 					sig_list();
1155 					break;
1156 				}
1157 				flag = sig_number(a1);
1158 				if(flag <0 || flag >= NSIG)
1159 					failed(cmd_name,badopt);
1160 				com++;
1161 				argn--;
1162 			}
1163 			while(--argn)
1164 			{
1165 				a1 = *++com;
1166 				exitval += job_kill(a1,flag);
1167 			}
1168 			break;
1169 		}
1170 #endif
1171 
1172 #ifdef apollo
1173 		/*
1174 		 *  Apollo system support library loads into the virtual address space
1175 		 */
1176 		case SYSINLIB:
1177 		{
1178 			int status,xfer;
1179 			short len;
1180 			std_$call void pm_$load();
1181 			std_$call void pm_$call();
1182 			if(a1)
1183 			{
1184 				len = strlen(a1);
1185 				pm_$load(*a1, len, 2 , 0, xfer,status);
1186 				if(status!=0)
1187 				failed(a1,"cannot inlib");
1188 				else if(xfer)
1189 					pm_$call(xfer);
1190 			}
1191 			break;
1192 		}
1193 
1194 		case SYSINPROCESS:
1195 			if(argn < 2)
1196 				on_option(INPROC);
1197 			else
1198 				exitval = exec_here(com);
1199 			break;
1200 #endif	/* apollo */
1201 	}
1202 }
1203 
1204 static const char flgchar[] = "efilmnprstuvwxzHLRZ";
1205 static const int flgval[] = {E_FLAG,F_FLAG,I_FLAG,L_FLAG,M_FLAG,
1206 			N_FLAG,P_FLAG,R_FLAG,S_FLAG,T_FLAG,U_FLAG,V_FLAG,
1207 			W_FLAG,X_FLAG,Z_FLAG,A_FLAG,L_JUST,R_JUST,R_JUST|Z_FILL};
1208 /*
1209  * process option flags for built-ins
1210  * flagmask are the invalid options
1211  */
1212 
1213 static int flagset(flaglist,flagmask)
1214 char *flaglist;
1215 {
1216 	register int flag = 0;
1217 	register int c;
1218 	register char *cp,*sp;
1219 
1220 	for(cp=flaglist+1;c = *cp;cp++)
1221 	{
1222 		if(isdigit(c))
1223 			wdnum = 10*wdnum + (c - '0');
1224 		else if(sp=strchr(flgchar,c))
1225 			flag |= flgval[sp-flgchar];
1226 		else
1227 			goto badoption;
1228 	}
1229 	if((flag&flagmask)==0)
1230 		return(flag);
1231 badoption:
1232 	failed(cmd_name,badopt);
1233 	/* NOTREACHED */
1234 }
1235 
1236 
1237 int execexp(s,f)
1238 register char *s;
1239 register FILE	*f;
1240 {
1241 	FILEBLK	fb;
1242 	FILE	fd;
1243 	TREPTR t;
1244 	char inbuf[BUFSIZ];
1245 	push(&fb);
1246 	if(s)
1247 	{
1248 		estabf(s,&fd);
1249 		fb.feval=(char **)(f);
1250 	}
1251 	else if(f!=NULL)
1252 	{
1253 		initf(f);
1254 		setbuf(f,inbuf);
1255 	}
1256 	exec_flag++;
1257 	t = cmd(NL,NLFLG|MTFLG);
1258 	exec_flag--;
1259 	if(is_option(READPR)==0)
1260 		states &= ~READPR;
1261 	if(s==NULL && fc_fix)
1262 		hist_flush();
1263 	pop(0);
1264 	execute(t,states&ERRFLG);
1265 }
1266 
1267 
1268 /*
1269  * Given the name or number of a signal return the signal number
1270  */
1271 
1272 static int sig_number(string)
1273 register char *string;
1274 {
1275 	register int n;
1276 	if(isdigit(*string))
1277 		n = atoi(string);
1278 	else
1279 	{
1280 		n = syslook(string,signal_names);
1281 		n &= (1<<SIGBITS)-1;
1282 		n--;
1283 	}
1284 	return(n);
1285 }
1286 
1287 #ifdef JOBS
1288 /*
1289  * list all the possible signals
1290  */
1291 static void sig_list()
1292 {
1293 	register SYSPTR	syscan;
1294 	register int n = MAXTRAP+1;
1295 	char *names[MAXTRAP+1];
1296 	syscan=signal_names;
1297 	p_setout(standout);
1298 	/* not all signals may be defined */
1299 	while(--n >= 0)
1300 		names[n] = badtrap;
1301 	while(*syscan->sysnam)
1302 	{
1303 		n = syscan->sysval;
1304 		n &= ((1<<SIGBITS)-1);
1305 		names[n] = syscan->sysnam;
1306 		syscan++;
1307 	}
1308 	n = MAXTRAP;
1309 	while(names[--n]==badtrap);
1310 	names[n+1] = NULL;
1311 	p_list(n-1,names+2);
1312 }
1313 #endif	/* JOBS */
1314