xref: /original-bsd/local/toolchest/ksh/sh/main.c (revision 74d2fb93)
1 /*
2 
3  *      Copyright (c) 1984, 1985, 1986 AT&T
4  *      All Rights Reserved
5 
6  *      THIS IS UNPUBLISHED PROPRIETARY SOURCE
7  *      CODE OF AT&T.
8  *      The copyright notice above does not
9  *      evidence any actual or intended
10  *      publication of such source code.
11 
12  */
13 /* @(#)main.c	1.1 */
14 /*
15  * UNIX shell
16  *
17  * S. R. Bourne
18  * Rewritten by David Korn
19  * AT&T Bell Laboratories
20  *
21  */
22 
23 #ifdef BSD
24 # ifdef BSD_4_2
25 # include	<sys/param.h>
26 # else
27 # include	<sys/types.h>
28 # endif /* BSD_4_2 */
29 #include   	 <sgtty.h>
30 #else
31 #include	<sys/types.h>
32 #endif /* BSD */
33 #include	<sys/stat.h>
34 #include	"defs.h"
35 #include	"sym.h"
36 #include	"flags.h"
37 #include	"io.h"
38 #include	"history.h"
39 #include	"mode.h"
40 #include	"name.h"
41 #include	"stak.h"
42 #include	"timeout.h"
43 #include	"brkincr.h"
44 #include	"builtins.h"
45 #ifdef pdp11
46 #include	<execargs.h>
47 #endif	/* pdp11 */
48 
49 #define FC_CHAR		'!'
50 
51 
52 /* These routines are defined by this module */
53 int	main();
54 void	chkpr();
55 char	*getpwd();
56 
57 /* These routines are referenced by this module */
58 extern void	addblok();
59 extern void	assign();
60 extern FILE	*chkopen();
61 extern TREPTR	cmd();
62 extern void	done();
63 extern void	fassign();
64 extern char	*getpwd();
65 extern void	gscan_some();
66 extern int	hist_open();
67 extern void	hist_close();
68 extern void	hist_eof();
69 extern void	hist_flush();
70 extern void	initf();
71 extern char	*itos();
72 extern char	*mactry();
73 extern char	*movstr();
74 extern void	mem_reinit();
75 extern void	meminit();
76 extern FILE	*pathopen();
77 extern void	p_flush();
78 extern void	p_num();
79 extern void	p_str();
80 extern void	p_setout();
81 extern void	rmlocal();
82 extern char	*strrchr();
83 extern void	settemp();
84 extern char	*simple();
85 extern void	stdsigs();
86 extern void	tdystak();
87 extern char	*valup();
88 
89 static void exfile();
90 static void pr_prompt();
91 static void chkmail();
92 
93 extern char **environ;
94 
95 
96 /* The following is defined for fixcmd */
97 
98 static struct stat lastmail;
99 static long mailtime;
100 static BOOL	beenhere = 0;
101 static int euserid;
102 static int egroupid;
103 
104 main(c, v)
105 int 	c;
106 register char *v[];
107 {
108 	FILE fd;
109 	register char *sim;
110 	register int 	rsflag=1;	/* local restricted flag */
111 #ifdef apollo
112 	extern char pm_$unix_env ;
113 	extern FILE *_iobmax;
114 	pm_$unix_env = -1;
115 	_iobmax = _iob + 20;
116 #endif	/* apollo */
117 	standout = stdout;
118 	p_setout(stderr);
119 	standin = &stdfile;
120 	userid=getuid();
121 	euserid=geteuid();
122 	egroupid=getegid();
123 	time(&mailtime);
124 	stdsigs();
125 	/* initialize storage allocation */
126 	stakbot = 0;
127 	addblok((unsigned)0);
128 	/* set up memory for namenods */
129 	meminit();
130 	/* set names from userenv
131 	 *  'rsflag' is zero if SHELL variable is
132 	 * set in environment and contains an'r' in
133 	 * the simple file part of the value.
134 	 */
135 
136 	rsflag=genenv();
137 	/* a shell is also restricted if argv(0) has
138 	 *  an 'rsh' for its simple name
139 	 */
140 	sim = simple(*v);
141 	if(*sim=='-')
142 	{
143 		sim++;
144 		login_sh = 2;
145 	}
146 	/* check for restricted shell */
147 	if(c>0 && gmatch(sim,"*rsh"))
148 		rsflag=0;
149 	/* look for options */
150 	/* dolc is $#	*/
151 	dolc=arg_opts(c,v);
152 	dolv=v+c-dolc--;
153 	dolv[0] = v[0];
154 	if(dolc < 1)
155 		on_option(STDFLG);
156 	if(is_option(STDFLG|CFLAG)==0)
157 	{
158 		dolc--;
159 		dolv++;
160 	}
161 	/* set[ug]id scripts run with the -p flag */
162 	if(userid!=euserid || getgid()!=egroupid)
163 	{
164 #ifdef BSD_4_2
165 		/* careful of #! setuid scripts with name beginning with - */
166 		if(login_sh && strcmp(v[0],v[1])==0)
167 			error(prohibited);
168 #endif /* BSD_4_2 */
169 		assign(PATHNOD,defpath);
170 		on_option(PRIVM);
171 	}
172 	if(is_option(RSHFLG))
173 	{
174 		rsflag = 0;
175 		off_option(RSHFLG);
176 	}
177 	/*
178 	 * return here for shell file execution
179 	 * but not for parenthesis subshells
180 	 */
181 	setjmp(subshell);
182 	cmdadr=dolv[0]; /* cmdadr is $0 */
183 	/* set pidname '$$' */
184 	movstr(itos(ppid=getpid()),pidadr);
185 	srand(ppid);
186 	ppid = getppid();
187 	settemp(pidadr);
188 	if((beenhere++)==0)
189 	{
190 		int prof = !is_option(PRIVM);
191 		/* decide whether shell is interactive */
192 		if(is_option(ONEFLG|CFLAG)==0 && is_option(STDFLG) && isatty(0) &&
193 				isatty(2))
194 			on_option(INTFLG);
195 		if(ppid==1)
196 			login_sh++;
197 		if(login_sh >= 2)
198 		{
199 			/* ? profile */
200 			login_sh += 2;
201 #ifdef JOBS
202 # ifdef BSD
203 			init_jobs(1);
204 # endif	/* BSD */
205 # ifdef SXT
206 			init_jobs(1);
207 # endif /* SXT */
208 #endif	/* JOBS */
209 			/*	system profile	*/
210 			if((input=pathopen(sysprofile)) != NULL)
211 				{
212 					exfile(TTYFLG);	/* file exists */
213 					fclose(input);
214 				}
215 			if(prof &&  (input=pathopen(mactry(profile))) != NULL)
216 			{
217 				exfile(TTYFLG);
218 				states &= ~TTYFLG;
219 				fclose(input);
220 			}
221 		}
222 		/* make sure PWD is set up correctly */
223 		if(getpwd(1))
224 			attrib(PWDNOD,N_EXPORT);
225 		if(prof)
226 		{
227 			if(sim = valup(ENVNOD))
228 				if(*(sim = mactry(sim)) == 0)
229 					sim = 0;
230 		}
231 		else
232 			sim = suid_profile;
233 		if(sim && (input = pathopen(sim)) != NULL)
234 		{
235 			exfile(TTYFLG);
236 			states &= ~TTYFLG;
237 			fclose(input);
238 		}
239 		if(rsflag==0)
240 			on_option(RSHFLG);
241 		/* open input file if specified */
242 		if(comdiv)
243 		{
244 			estabf(comdiv,&fd);
245 		}
246 		else
247 		{
248 			sim = cmdadr;
249 			cmdadr = v[0];
250 			if(is_option(STDFLG))
251 				input = stdin;
252 			else
253 			{
254 #ifdef VFORK
255 				char *sp = sim;
256 				/* avoid path search if we already have the
257 				 * path name in the environment
258 				 */
259 				if(strcmp(sim,simple(*environ))==0)
260 					sp = (*environ)+2;
261 #endif /* VFORK */
262 #ifdef SUID_EXEC
263 				/* open stream should have been passed into shell */
264 				if(gmatch(sim,devfdNN))
265 				{
266 					struct stat statb;
267 					int fd = atoi(sim+8);
268 					if(fstat(fd,&statb)<0)
269 						failed(cmdadr,badopen);
270 					input = fdopen(fd,"r");
271 					sim = cmdadr;
272 					off_option(EXECPR|READPR);
273 				}
274 				else
275 #endif /* SUID_EXEC */
276 #ifdef VFORK
277 				if((input=pathopen(sp))==NULL)
278 #else
279 				if((input=pathopen(sim))==NULL)
280 					/* for compatibility with bsh */
281 					if((input=chkopen(sim))==NULL)
282 #endif /* VFORK */
283 						failed(sim,badopen);
284 				/* eliminate local aliases and functions */
285 				gscan_some(rmlocal,alias,N_EXPORT,0);
286 				gscan_some(rmlocal,prnames,N_EXPORT,0);
287 			}
288 			cmdadr = sim;
289 			comdiv--;
290 #ifdef ACCT
291 			initacct();
292 			if(fileno(input) != 0)
293 				preacct(cmdadr);
294 #endif	/* ACCT */
295 		}
296 	}
297 #ifdef pdp11
298 	else
299 		*execargs=(char *) dolv;	/* for `ps' cmd */
300 #endif	/* pdp11 */
301 	states |= is_option(INTFLG);
302 	exfile(0);
303 	if(states&PROMPT)
304 	        newline();
305 	done(0);
306 }
307 
308 static void	exfile(prof)
309 register int prof;
310 {
311 	long curtime;
312 	TREPTR t;
313 	register FILE *fp = input;
314 	register struct fixcmd *fixp;
315 	register int fno;
316 	unsigned execflags;
317 	/* move input */
318 	if(fisopen(fp) && (fno=fileno(fp))!=F_STRING)
319 	{
320 		if(fno > 0)
321 		{
322 			fp = input = frenumber(fp,INIO);
323 			setbuf(fp,(char*)_sibuf);
324 			if(prof==0)
325 				setbuf(stdin,NIL);
326 		}
327 		/* if stdin is a pipe it must be unbuffered */
328 		else
329 			setbuf(fp,ispipe(fp)?NIL:(char*)_sibuf);
330 	}
331 	if(states&INTFLG)
332 	{
333 		if(isnull(PS1NOD))
334 			assign(PS1NOD, (euserid?stdprompt:supprompt));
335 		states |= TTYFLG|PROMPT;
336 		ignsig(SIGTERM);
337 		hist_open();
338 		if(fc_fix)
339 			on_option(FIXFLG);
340 #ifdef JOBS
341 # ifdef BSD
342 		if(login_sh<=1)
343 			init_jobs(0);
344 # endif	/* BSD */
345 # ifdef SXT
346 		if(login_sh<=1)
347 			init_jobs(0);
348 # endif /* SXT */
349 #endif	/* JOBS */
350 	}
351 	else
352 	{
353 		if(prof)
354 			states |= prof;
355 		else
356 		{
357 			off_option(EDITVI|EMACS|GMACS);
358 			on_option(HASHALL|INPROC);
359 		}
360 		states &= ~(PROMPT|MONITOR);
361 		off_option(FIXFLG);
362 	}
363 	fixp = fc_fix;
364 	if(setjmp(errshell) && prof)
365 	{
366 		fclose(fp);
367 		return;
368 	}
369 	/* error return here */
370 	freturn = (jmp_buf*)errshell;
371 	loopcnt=peekc=peekn=0;
372 	iopend=0;
373 	linked = 0;
374 	if(fp!=NULL)
375 		initf(fp);
376 	if(feof(fp))
377 		goto eof_or_error;
378 	/* command loop */
379 	while(1)
380 	{
381 		tdystak((STKPTR)0);
382 		stakchk(); /* may reduce sbrk */
383 		exitset();
384 		states &= ~(ERRFLG|READPR|RWAIT|MONITOR);
385 		states |= is_option(READPR)|WAITING|ERRFLG;
386 		/* -eim  flags don't apply to profiles */
387 		if(prof)
388 			states &= ~(INTFLG|ERRFLG|MONITOR);
389 		p_setout(stderr);
390 		if((states&PROMPT) && standin->fstak==0 && !feof(fp))
391 		{
392 			register char *mail;
393 #ifdef JOBS
394 			/* allow children to be stopped*/
395 			states &= ~(MONITOR|NONSTOP);
396 			states |= is_option(MONITOR);
397 			list_jobs(N_FLAG);
398 #endif	/* JOBS */
399 			if((mail=valup(MAILPNOD)) || (mail=valup(MAILNOD)))
400 			{
401 				time(&curtime);
402 				if ((curtime - mailtime) >= mailchk)
403 				{
404 					chkmail(mail);
405 					mailtime = curtime;
406 				}
407 			}
408 			if(fixp)
409 				hist_eof();
410 			pr_prompt(mactry(valup(PS1NOD)));
411 			/* sets timeout for command entry */
412 #if TIMEOUT!=0
413 			if(timeout <= 0 || timeout > TIMEOUT)
414 				timeout = TIMEOUT;
415 #endif
416 			if(timeout>0)
417 				alarm((unsigned)timeout);
418 			standin->flin = 1;
419 		}
420 		trapnote=0;
421 		peekc=readc();
422 		if(feof(fp) || ferror(fp))
423 		{
424 		eof_or_error:
425 			if((states&PROMPT) && standin->fstak==0 && ferror(fp)==0)
426 			{
427 				clearerr(fp);
428 				if(is_option(NOEOF) && !ferror(output))
429 				{
430 					p_str(logout,NL);
431 					continue;
432 				}
433 #ifdef JOBS
434 				else if(close_jobs()<0)
435 					continue;
436 #endif	/* JOBS */
437 				hist_close();
438 			}
439 			return;
440 		}
441 		if(timeout>0)
442 			alarm(0);
443 		if((states&PROMPT) && fixp)
444 		{
445 			hist_eof();
446 			putc(peekc,fixp->fixfd);
447 		}
448 		states |= is_option(FIXFLG);
449 		states &= ~ WAITING;
450 		t = cmd(NL,MTFLG);
451 		if(fixp)
452 			hist_flush();
453 		if(t)
454 		{
455 			execflags = ERRFLG;
456 			/* sh -c simple-command may not have to fork */
457 			if(prof==0 && is_option(CFLAG) && (t->tretyp&COMMSK)==TCOM
458 				&& nextchar(fp)==0)
459 			{
460 				execflags |= 1;
461 			}
462 			execute(t,execflags);
463 		}
464 	}
465 }
466 
467 /*
468  * if there is pending input, the prompt is not printed.
469  * prints PS2 if flag is zero otherwise PS3
470  */
471 
472 void	chkpr(flag)
473 register int flag;
474 {
475 	if(flag || (states&PROMPT) && standin->fstak==0)
476 	{
477 		/* if characters are in input buffer don't issue prompt */
478 		if((flag?stdin:input)->_cnt > 0)
479 			return;
480 		p_setout(stderr);
481 		fputs(valup(flag?PS3NOD:PS2NOD),output);
482 	}
483 }
484 
485 
486 
487 /* prints out messages if files in list have been modified since last call */
488 static void chkmail(files)
489 char *files;
490 {
491 	register char *cp,*sp,*qp;
492 	register char save;
493 	struct stat	statb;
494 	if(*(cp=files) == 0)
495 		return;
496 	sp = cp;
497 	do
498 	{
499 		/* skip to : or end of string saving first '?' */
500 		for(qp=0;*sp && *sp != ':';sp++)
501 			if((*sp == '?' || *sp=='%') && qp == 0)
502 				qp = sp;
503 		save = *sp;
504 		*sp = 0;
505 		/* change '?' to end-of-string */
506 		if(qp)
507 			*qp = 0;
508 		gchain = NULL;
509 		do
510 		{
511 			/* see if time has been modified since last checked
512 			 * and the access time <= the modification time
513 			 */
514 			if(stat(cp,&statb) >= 0 && statb.st_mtime >= mailtime
515 				&& statb.st_atime <= statb.st_mtime)
516 			{
517 				/* check for directory */
518 				if(gchain==NULL && (statb.st_mode&S_IFMT)==S_IFDIR)
519 				{
520 					/* generate list of directory entries */
521 					f_complete(cp,"/*");
522 				}
523 				else
524 				{
525 					/*
526 					 * If the file has shrunk,
527 					 * or if the size is zero
528 					 * then don't print anything
529 					 */
530 					if(statb.st_size &&
531 						(  statb.st_ino != lastmail.st_ino
532 						|| statb.st_dev != lastmail.st_dev
533 						|| statb.st_size > lastmail.st_size))
534 					{
535 						/* save and restore $_ */
536 						char *save = lastarg;
537 						lastarg = cp;
538 						p_str(mactry(qp==0?mailmsg:qp+1),NL);
539 						lastarg = save;
540 					}
541 					lastmail = statb;
542 					break;
543 				}
544 			}
545 			if(gchain)
546 			{
547 				cp = gchain->argval;
548 				gchain = gchain->argchn;
549 			}
550 			else
551 				cp = NULL;
552 		}
553 		while(cp);
554 		if(qp)
555 			*qp = '?';
556 		*sp++ = save;
557 		cp = sp;
558 	}
559 	while(save);
560 	tdystak((STKPTR)0);
561 }
562 
563 /*
564  * print the primary prompt
565  */
566 
567 static void pr_prompt(string)
568 register char *string;
569 {
570 	register char *cp;
571 	register int c;
572 	static short cmdno;
573 #ifdef BSD
574 	int mode;
575 #include	<sys/ioctl.h>
576 	mode = LFLUSHO;
577 	ioctl(fileno(output),TIOCLBIC,&mode);
578 #endif	/* BSD */
579 	p_flush();
580 	p_setout(stderr);
581 	for(cp=string;c= *cp;cp++)
582 	{
583 		if(c==FC_CHAR)
584 		{
585 			/* look at next character */
586 			c = *++cp;
587 			/* print out line number if not !! */
588 			if(c!= FC_CHAR)
589 				p_num(fc_fix?fc_fix->fixind:++cmdno,0);
590 			if(c==0)
591 				break;
592 		}
593 		putc(c,output);
594 	}
595 #if VSH || ESH
596 	*output->_ptr = 0;
597 	/* prompt flushed later */
598 #else
599 	p_flush();
600 #endif
601 }
602 
603 /* make sure PWD is set up correctly */
604 
605 /*
606  * Return the value of the PWD variable
607  * Invokes /bin/pwd if necessary
608  * If mode non-zero, then PWD must have same device/inode as dot
609  */
610 
611 char *getpwd(mode)
612 {
613 	register char *sim;
614 	register int count = 0;
615 	while((sim=valup(PWDNOD))==NULL || *sim==0 || (mode&&!eq_inode(sim,dot)))
616 	{
617 		if(count++ > 0)
618 			return(NULL);
619 		if((sim=valup(HOME)) && *sim=='/' && eq_inode(sim,dot))
620 		{
621 			fassign(PWDNOD,sim);
622 			break;
623 		}
624 		else
625 		{
626 #ifdef apollo
627 			char buff[BUFSIZ+1];
628 			extern char *getcwd();
629 			sim=getcwd(buff+1,BUFSIZ);
630 			if(buff[1]=='/' && buff[2]=='/')
631 			{
632 				buff[0]='/';
633 				buff[1] = 'n';
634 				sim = buff;
635 			}
636 			fassign(PWDNOD,sim);
637 #else
638 			/* even restricted shells can pwd */
639 			optflag savflags = flags;
640 			off_option(RSHFLG);
641 			execexp(setpwd,(FILE*)0);
642 			flags = savflags;
643 #endif	/* apollo */
644 		}
645 	}
646 	return(sim);
647 }
648 
649 
650 /*
651  * This version of access checks against effective uid/gid
652  */
653 
654 access(name, mode)
655 register char	*name;
656 register int mode;
657 {
658 	struct stat statb;
659 	if (stat(name, &statb) == 0)
660 	{
661 		if(mode == 0)
662 			return(mode);
663 		else if(euserid == 0)
664 		{
665 			if((statb.st_mode&S_IFMT) != S_IFREG || mode != 1)
666 				return(0);
667 		    	/* root needs execute permission for someone */
668 			mode = (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6));
669 		}
670 		else if(euserid == statb.st_uid)
671 			mode <<= 6;
672 		else if(egroupid == statb.st_gid)
673 			mode <<= 3;
674 #ifdef BSD
675 # ifdef BSD_4_2
676 		/* in BSD_4_2 you can be in several groups */
677 		else
678 		{
679 			int groups[NGROUPS];
680 			register int n;
681 			n = getgroups(NGROUPS,groups);
682 			while(--n >= 0)
683 			{
684 				if(groups[n] == statb.st_gid)
685 				{
686 					mode <<= 3;
687 					break;
688 				}
689 			}
690 		}
691 # endif /* BSD_4_2 */
692 #endif /* BSD */
693 		if(statb.st_mode & mode)
694 			return(0);
695 	}
696 	return(-1);
697 }
698 
699