main.c revision 64702
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 3. All advertising materials mentioning features or use of this software
171556Srgrimes *    must display the following acknowledgement:
181556Srgrimes *	This product includes software developed by the University of
191556Srgrimes *	California, Berkeley and its contributors.
201556Srgrimes * 4. Neither the name of the University nor the names of its contributors
211556Srgrimes *    may be used to endorse or promote products derived from this software
221556Srgrimes *    without specific prior written permission.
231556Srgrimes *
241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341556Srgrimes * SUCH DAMAGE.
351556Srgrimes */
361556Srgrimes
371556Srgrimes#ifndef lint
3820425Sstevestatic char const copyright[] =
391556Srgrimes"@(#) Copyright (c) 1991, 1993\n\
401556Srgrimes	The Regents of the University of California.  All rights reserved.\n";
411556Srgrimes#endif /* not lint */
421556Srgrimes
431556Srgrimes#ifndef lint
4436150Scharnier#if 0
4536150Scharnierstatic char sccsid[] = "@(#)main.c	8.6 (Berkeley) 5/28/95";
4636150Scharnier#endif
4736150Scharnierstatic const char rcsid[] =
4850471Speter  "$FreeBSD: head/bin/sh/main.c 64702 2000-08-16 10:39:43Z cracauer $";
491556Srgrimes#endif /* not lint */
501556Srgrimes
5117987Speter#include <stdio.h>
521556Srgrimes#include <signal.h>
5317987Speter#include <sys/stat.h>
5417987Speter#include <unistd.h>
551556Srgrimes#include <fcntl.h>
5617525Sache#include <locale.h>
5759214Simp#include <errno.h>
5817525Sache
591556Srgrimes#include "shell.h"
601556Srgrimes#include "main.h"
611556Srgrimes#include "mail.h"
621556Srgrimes#include "options.h"
631556Srgrimes#include "output.h"
641556Srgrimes#include "parser.h"
651556Srgrimes#include "nodes.h"
6617987Speter#include "expand.h"
671556Srgrimes#include "eval.h"
681556Srgrimes#include "jobs.h"
691556Srgrimes#include "input.h"
701556Srgrimes#include "trap.h"
711556Srgrimes#include "var.h"
7217987Speter#include "show.h"
731556Srgrimes#include "memalloc.h"
741556Srgrimes#include "error.h"
751556Srgrimes#include "init.h"
761556Srgrimes#include "mystring.h"
7717987Speter#include "exec.h"
7820425Ssteve#include "cd.h"
791556Srgrimes
801556Srgrimes#define PROFILE 0
811556Srgrimes
821556Srgrimesint rootpid;
831556Srgrimesint rootshell;
841556Srgrimes#if PROFILE
851556Srgrimesshort profile_buf[16384];
861556Srgrimesextern int etext();
871556Srgrimes#endif
881556Srgrimes
8917987SpeterSTATIC void read_profile __P((char *));
9017987SpeterSTATIC char *find_dot_file __P((char *));
911556Srgrimes
921556Srgrimes/*
931556Srgrimes * Main routine.  We initialize things, parse the arguments, execute
941556Srgrimes * profiles if we're a login shell, and then call cmdloop to execute
951556Srgrimes * commands.  The setjmp call sets up the location to jump to when an
961556Srgrimes * exception occurs.  When an exception occurs the variable "state"
971556Srgrimes * is used to figure out how far we had gotten.
981556Srgrimes */
991556Srgrimes
10017987Speterint
10117987Spetermain(argc, argv)
10217987Speter	int argc;
10320425Ssteve	char **argv;
10417987Speter{
1051556Srgrimes	struct jmploc jmploc;
1061556Srgrimes	struct stackmark smark;
1071556Srgrimes	volatile int state;
1081556Srgrimes	char *shinit;
1091556Srgrimes
1101556Srgrimes#if PROFILE
1111556Srgrimes	monitor(4, etext, profile_buf, sizeof profile_buf, 50);
1121556Srgrimes#endif
11317525Sache	(void) setlocale(LC_ALL, "");
1141556Srgrimes	state = 0;
1151556Srgrimes	if (setjmp(jmploc.loc)) {
1161556Srgrimes		/*
1171556Srgrimes		 * When a shell procedure is executed, we raise the
1181556Srgrimes		 * exception EXSHELLPROC to clean up before executing
1191556Srgrimes		 * the shell procedure.
1201556Srgrimes		 */
12120425Ssteve		switch (exception) {
12220425Ssteve		case EXSHELLPROC:
1231556Srgrimes			rootpid = getpid();
1241556Srgrimes			rootshell = 1;
1251556Srgrimes			minusc = NULL;
1261556Srgrimes			state = 3;
12720425Ssteve			break;
12820425Ssteve
12920425Ssteve		case EXEXEC:
13020425Ssteve			exitstatus = exerrno;
13120425Ssteve			break;
13220425Ssteve
13320425Ssteve		case EXERROR:
13420425Ssteve			exitstatus = 2;
13520425Ssteve			break;
13620425Ssteve
13720425Ssteve		default:
13820425Ssteve			break;
13920425Ssteve		}
14020425Ssteve
14120425Ssteve		if (exception != EXSHELLPROC) {
14220425Ssteve		    if (state == 0 || iflag == 0 || ! rootshell)
14320425Ssteve			    exitshell(exitstatus);
14420425Ssteve		}
1451556Srgrimes		reset();
1461556Srgrimes		if (exception == EXINT
1471556Srgrimes#if ATTY
1481556Srgrimes		 && (! attyset() || equal(termval(), "emacs"))
1491556Srgrimes#endif
1501556Srgrimes		 ) {
1511556Srgrimes			out2c('\n');
1521556Srgrimes			flushout(&errout);
1531556Srgrimes		}
1541556Srgrimes		popstackmark(&smark);
1551556Srgrimes		FORCEINTON;				/* enable interrupts */
1561556Srgrimes		if (state == 1)
1571556Srgrimes			goto state1;
1581556Srgrimes		else if (state == 2)
1591556Srgrimes			goto state2;
1601556Srgrimes		else if (state == 3)
1611556Srgrimes			goto state3;
1621556Srgrimes		else
1631556Srgrimes			goto state4;
1641556Srgrimes	}
1651556Srgrimes	handler = &jmploc;
1661556Srgrimes#ifdef DEBUG
1671556Srgrimes	opentrace();
1681556Srgrimes	trputs("Shell args:  ");  trargs(argv);
1691556Srgrimes#endif
1701556Srgrimes	rootpid = getpid();
1711556Srgrimes	rootshell = 1;
1721556Srgrimes	init();
1731556Srgrimes	setstackmark(&smark);
1741556Srgrimes	procargs(argc, argv);
17520774Ssteve	if (getpwd() == NULL && iflag)
17620774Ssteve		out2str("sh: cannot determine working directory\n");
1771556Srgrimes	if (argv[0] && argv[0][0] == '-') {
1781556Srgrimes		state = 1;
1791556Srgrimes		read_profile("/etc/profile");
1801556Srgrimesstate1:
1811556Srgrimes		state = 2;
18219240Ssteve		if (privileged == 0)
18319240Ssteve			read_profile(".profile");
18419240Ssteve		else
18519240Ssteve			read_profile("/etc/suid_profile");
1868855Srgrimes	}
1871556Srgrimesstate2:
1881556Srgrimes	state = 3;
18925471Ssteve	if (!privileged && iflag) {
19017987Speter		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
19117987Speter			state = 3;
19217987Speter			read_profile(shinit);
19317987Speter		}
1941556Srgrimes	}
1951556Srgrimesstate3:
1961556Srgrimes	state = 4;
1971556Srgrimes	if (minusc) {
1981556Srgrimes		evalstring(minusc);
1991556Srgrimes	}
2001556Srgrimes	if (sflag || minusc == NULL) {
2011556Srgrimesstate4:	/* XXX ??? - why isn't this before the "if" statement */
2021556Srgrimes		cmdloop(1);
2031556Srgrimes	}
2041556Srgrimes#if PROFILE
2051556Srgrimes	monitor(0);
2061556Srgrimes#endif
2071556Srgrimes	exitshell(exitstatus);
20817987Speter	/*NOTREACHED*/
20917987Speter	return 0;
2101556Srgrimes}
2111556Srgrimes
2121556Srgrimes
2131556Srgrimes/*
2141556Srgrimes * Read and execute commands.  "Top" is nonzero for the top level command
2151556Srgrimes * loop; it turns on prompting if the shell is interactive.
2161556Srgrimes */
2171556Srgrimes
2181556Srgrimesvoid
21920425Sstevecmdloop(top)
22017987Speter	int top;
22117987Speter{
2221556Srgrimes	union node *n;
2231556Srgrimes	struct stackmark smark;
2241556Srgrimes	int inter;
2251556Srgrimes	int numeof = 0;
2261556Srgrimes
2271556Srgrimes	TRACE(("cmdloop(%d) called\n", top));
2281556Srgrimes	setstackmark(&smark);
2291556Srgrimes	for (;;) {
2301556Srgrimes		if (pendingsigs)
2311556Srgrimes			dotrap();
2321556Srgrimes		inter = 0;
2331556Srgrimes		if (iflag && top) {
2341556Srgrimes			inter++;
2351556Srgrimes			showjobs(1);
2361556Srgrimes			chkmail(0);
2371556Srgrimes			flushout(&output);
2381556Srgrimes		}
2391556Srgrimes		n = parsecmd(inter);
2401556Srgrimes		/* showtree(n); DEBUG */
2411556Srgrimes		if (n == NEOF) {
2421556Srgrimes			if (!top || numeof >= 50)
2431556Srgrimes				break;
2441556Srgrimes			if (!stoppedjobs()) {
2451556Srgrimes				if (!Iflag)
2461556Srgrimes					break;
2471556Srgrimes				out2str("\nUse \"exit\" to leave shell.\n");
2481556Srgrimes			}
2491556Srgrimes			numeof++;
2501556Srgrimes		} else if (n != NULL && nflag == 0) {
2511556Srgrimes			job_warning = (job_warning == 2) ? 1 : 0;
2521556Srgrimes			numeof = 0;
2531556Srgrimes			evaltree(n, 0);
2541556Srgrimes		}
2551556Srgrimes		popstackmark(&smark);
25664702Scracauer		setstackmark(&smark);
25720425Ssteve		if (evalskip == SKIPFILE) {
25820425Ssteve			evalskip = 0;
25920425Ssteve			break;
26020425Ssteve		}
2611556Srgrimes	}
26264702Scracauer	popstackmark(&smark);
2631556Srgrimes}
2641556Srgrimes
2651556Srgrimes
2661556Srgrimes
2671556Srgrimes/*
2681556Srgrimes * Read /etc/profile or .profile.  Return on error.
2691556Srgrimes */
2701556Srgrimes
2711556SrgrimesSTATIC void
2721556Srgrimesread_profile(name)
2731556Srgrimes	char *name;
2741556Srgrimes	{
2751556Srgrimes	int fd;
2761556Srgrimes
2771556Srgrimes	INTOFF;
2781556Srgrimes	if ((fd = open(name, O_RDONLY)) >= 0)
2791556Srgrimes		setinputfd(fd, 1);
2801556Srgrimes	INTON;
2811556Srgrimes	if (fd < 0)
2821556Srgrimes		return;
2831556Srgrimes	cmdloop(0);
2841556Srgrimes	popfile();
2851556Srgrimes}
2861556Srgrimes
2871556Srgrimes
2881556Srgrimes
2891556Srgrimes/*
2901556Srgrimes * Read a file containing shell functions.
2911556Srgrimes */
2921556Srgrimes
2931556Srgrimesvoid
2941556Srgrimesreadcmdfile(name)
2951556Srgrimes	char *name;
29617987Speter{
2971556Srgrimes	int fd;
2981556Srgrimes
2991556Srgrimes	INTOFF;
3001556Srgrimes	if ((fd = open(name, O_RDONLY)) >= 0)
3011556Srgrimes		setinputfd(fd, 1);
3021556Srgrimes	else
30353891Scracauer		error("Can't open %s: %s", name, strerror(errno));
3041556Srgrimes	INTON;
3051556Srgrimes	cmdloop(0);
3061556Srgrimes	popfile();
3071556Srgrimes}
3081556Srgrimes
3091556Srgrimes
3101556Srgrimes
3111556Srgrimes/*
31246684Skris * Take commands from a file.  To be compatible we should do a path
31317987Speter * search for the file, which is necessary to find sub-commands.
3141556Srgrimes */
3151556Srgrimes
31617987Speter
31717987SpeterSTATIC char *
31817987Speterfind_dot_file(basename)
31917987Speter	char *basename;
32017987Speter{
32117987Speter	static char localname[FILENAME_MAX+1];
32217987Speter	char *fullname;
32317987Speter	char *path = pathval();
32417987Speter	struct stat statb;
32517987Speter
32617987Speter	/* don't try this for absolute or relative paths */
32717987Speter	if( strchr(basename, '/'))
32817987Speter		return basename;
32917987Speter
33017987Speter	while ((fullname = padvance(&path, basename)) != NULL) {
33117987Speter		strcpy(localname, fullname);
33217987Speter		stunalloc(fullname);
33317987Speter		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode))
33417987Speter			return localname;
33517987Speter	}
33617987Speter	return basename;
33717987Speter}
33817987Speter
33917987Speterint
34020425Sstevedotcmd(argc, argv)
34117987Speter	int argc;
34220425Ssteve	char **argv;
34317987Speter{
34417987Speter	struct strlist *sp;
3451556Srgrimes	exitstatus = 0;
34617987Speter
34717987Speter	for (sp = cmdenviron; sp ; sp = sp->next)
34817987Speter		setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED);
34917987Speter
3501556Srgrimes	if (argc >= 2) {		/* That's what SVR2 does */
35117987Speter		char *fullname = find_dot_file(argv[1]);
35217987Speter
35317987Speter		setinputfile(fullname, 1);
35417987Speter		commandname = fullname;
3551556Srgrimes		cmdloop(0);
3561556Srgrimes		popfile();
3571556Srgrimes	}
3581556Srgrimes	return exitstatus;
3591556Srgrimes}
3601556Srgrimes
3611556Srgrimes
36217987Speterint
36320425Ssteveexitcmd(argc, argv)
36417987Speter	int argc;
36520425Ssteve	char **argv;
36617987Speter{
36718267Sadam	extern int oexitstatus;
36818267Sadam
3691556Srgrimes	if (stoppedjobs())
37017987Speter		return 0;
37120425Ssteve	if (argc > 1)
37220425Ssteve		exitstatus = number(argv[1]);
37320425Ssteve	else
37420425Ssteve		exitstatus = oexitstatus;
37518254Sbde	exitshell(exitstatus);
37617987Speter	/*NOTREACHED*/
37717987Speter	return 0;
3781556Srgrimes}
3791556Srgrimes
3801556Srgrimes
3811556Srgrimes#ifdef notdef
3821556Srgrimes/*
3831556Srgrimes * Should never be called.
3841556Srgrimes */
3851556Srgrimes
3861556Srgrimesvoid
3871556Srgrimesexit(exitstatus) {
3881556Srgrimes	_exit(exitstatus);
3891556Srgrimes}
3901556Srgrimes#endif
391