main.c revision 99110
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
471556Srgrimes#endif /* not lint */
4899110Sobrien#include <sys/cdefs.h>
4999110Sobrien__FBSDID("$FreeBSD: head/bin/sh/main.c 99110 2002-06-30 05:15:05Z obrien $");
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
8990111SimpSTATIC void read_profile(char *);
9090111SimpSTATIC char *find_dot_file(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
10190111Simpmain(int argc, char *argv[])
10217987Speter{
1031556Srgrimes	struct jmploc jmploc;
1041556Srgrimes	struct stackmark smark;
1051556Srgrimes	volatile int state;
1061556Srgrimes	char *shinit;
1071556Srgrimes
1081556Srgrimes#if PROFILE
1091556Srgrimes	monitor(4, etext, profile_buf, sizeof profile_buf, 50);
1101556Srgrimes#endif
11117525Sache	(void) setlocale(LC_ALL, "");
1121556Srgrimes	state = 0;
1131556Srgrimes	if (setjmp(jmploc.loc)) {
1141556Srgrimes		/*
1151556Srgrimes		 * When a shell procedure is executed, we raise the
1161556Srgrimes		 * exception EXSHELLPROC to clean up before executing
1171556Srgrimes		 * the shell procedure.
1181556Srgrimes		 */
11920425Ssteve		switch (exception) {
12020425Ssteve		case EXSHELLPROC:
1211556Srgrimes			rootpid = getpid();
1221556Srgrimes			rootshell = 1;
1231556Srgrimes			minusc = NULL;
1241556Srgrimes			state = 3;
12520425Ssteve			break;
12620425Ssteve
12720425Ssteve		case EXEXEC:
12820425Ssteve			exitstatus = exerrno;
12920425Ssteve			break;
13020425Ssteve
13120425Ssteve		case EXERROR:
13220425Ssteve			exitstatus = 2;
13320425Ssteve			break;
13420425Ssteve
13520425Ssteve		default:
13620425Ssteve			break;
13720425Ssteve		}
13820425Ssteve
13920425Ssteve		if (exception != EXSHELLPROC) {
14020425Ssteve		    if (state == 0 || iflag == 0 || ! rootshell)
14120425Ssteve			    exitshell(exitstatus);
14220425Ssteve		}
1431556Srgrimes		reset();
1441556Srgrimes		if (exception == EXINT
1451556Srgrimes#if ATTY
1461556Srgrimes		 && (! attyset() || equal(termval(), "emacs"))
1471556Srgrimes#endif
1481556Srgrimes		 ) {
1491556Srgrimes			out2c('\n');
1501556Srgrimes			flushout(&errout);
1511556Srgrimes		}
1521556Srgrimes		popstackmark(&smark);
1531556Srgrimes		FORCEINTON;				/* enable interrupts */
1541556Srgrimes		if (state == 1)
1551556Srgrimes			goto state1;
1561556Srgrimes		else if (state == 2)
1571556Srgrimes			goto state2;
1581556Srgrimes		else if (state == 3)
1591556Srgrimes			goto state3;
1601556Srgrimes		else
1611556Srgrimes			goto state4;
1621556Srgrimes	}
1631556Srgrimes	handler = &jmploc;
1641556Srgrimes#ifdef DEBUG
1651556Srgrimes	opentrace();
1661556Srgrimes	trputs("Shell args:  ");  trargs(argv);
1671556Srgrimes#endif
1681556Srgrimes	rootpid = getpid();
1691556Srgrimes	rootshell = 1;
1701556Srgrimes	init();
1711556Srgrimes	setstackmark(&smark);
1721556Srgrimes	procargs(argc, argv);
17320774Ssteve	if (getpwd() == NULL && iflag)
17420774Ssteve		out2str("sh: cannot determine working directory\n");
1751556Srgrimes	if (argv[0] && argv[0][0] == '-') {
1761556Srgrimes		state = 1;
1771556Srgrimes		read_profile("/etc/profile");
1781556Srgrimesstate1:
1791556Srgrimes		state = 2;
18019240Ssteve		if (privileged == 0)
18119240Ssteve			read_profile(".profile");
18219240Ssteve		else
18319240Ssteve			read_profile("/etc/suid_profile");
1848855Srgrimes	}
1851556Srgrimesstate2:
1861556Srgrimes	state = 3;
18725471Ssteve	if (!privileged && iflag) {
18817987Speter		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
18917987Speter			state = 3;
19017987Speter			read_profile(shinit);
19117987Speter		}
1921556Srgrimes	}
1931556Srgrimesstate3:
1941556Srgrimes	state = 4;
1951556Srgrimes	if (minusc) {
1961556Srgrimes		evalstring(minusc);
1971556Srgrimes	}
1981556Srgrimes	if (sflag || minusc == NULL) {
1991556Srgrimesstate4:	/* XXX ??? - why isn't this before the "if" statement */
2001556Srgrimes		cmdloop(1);
2011556Srgrimes	}
2021556Srgrimes#if PROFILE
2031556Srgrimes	monitor(0);
2041556Srgrimes#endif
2051556Srgrimes	exitshell(exitstatus);
20617987Speter	/*NOTREACHED*/
20717987Speter	return 0;
2081556Srgrimes}
2091556Srgrimes
2101556Srgrimes
2111556Srgrimes/*
2121556Srgrimes * Read and execute commands.  "Top" is nonzero for the top level command
2131556Srgrimes * loop; it turns on prompting if the shell is interactive.
2141556Srgrimes */
2151556Srgrimes
2161556Srgrimesvoid
21790111Simpcmdloop(int top)
21817987Speter{
2191556Srgrimes	union node *n;
2201556Srgrimes	struct stackmark smark;
2211556Srgrimes	int inter;
2221556Srgrimes	int numeof = 0;
2231556Srgrimes
2241556Srgrimes	TRACE(("cmdloop(%d) called\n", top));
2251556Srgrimes	setstackmark(&smark);
2261556Srgrimes	for (;;) {
2271556Srgrimes		if (pendingsigs)
2281556Srgrimes			dotrap();
2291556Srgrimes		inter = 0;
2301556Srgrimes		if (iflag && top) {
2311556Srgrimes			inter++;
23297669Stjr			showjobs(1, 0, 0);
2331556Srgrimes			chkmail(0);
2341556Srgrimes			flushout(&output);
2351556Srgrimes		}
2361556Srgrimes		n = parsecmd(inter);
2371556Srgrimes		/* showtree(n); DEBUG */
2381556Srgrimes		if (n == NEOF) {
2391556Srgrimes			if (!top || numeof >= 50)
2401556Srgrimes				break;
2411556Srgrimes			if (!stoppedjobs()) {
2421556Srgrimes				if (!Iflag)
2431556Srgrimes					break;
2441556Srgrimes				out2str("\nUse \"exit\" to leave shell.\n");
2451556Srgrimes			}
2461556Srgrimes			numeof++;
2471556Srgrimes		} else if (n != NULL && nflag == 0) {
2481556Srgrimes			job_warning = (job_warning == 2) ? 1 : 0;
2491556Srgrimes			numeof = 0;
2501556Srgrimes			evaltree(n, 0);
2511556Srgrimes		}
2521556Srgrimes		popstackmark(&smark);
25364702Scracauer		setstackmark(&smark);
25420425Ssteve		if (evalskip == SKIPFILE) {
25520425Ssteve			evalskip = 0;
25620425Ssteve			break;
25720425Ssteve		}
2581556Srgrimes	}
25964702Scracauer	popstackmark(&smark);
2601556Srgrimes}
2611556Srgrimes
2621556Srgrimes
2631556Srgrimes
2641556Srgrimes/*
2651556Srgrimes * Read /etc/profile or .profile.  Return on error.
2661556Srgrimes */
2671556Srgrimes
2681556SrgrimesSTATIC void
26990111Simpread_profile(char *name)
27090111Simp{
2711556Srgrimes	int fd;
2721556Srgrimes
2731556Srgrimes	INTOFF;
2741556Srgrimes	if ((fd = open(name, O_RDONLY)) >= 0)
2751556Srgrimes		setinputfd(fd, 1);
2761556Srgrimes	INTON;
2771556Srgrimes	if (fd < 0)
2781556Srgrimes		return;
2791556Srgrimes	cmdloop(0);
2801556Srgrimes	popfile();
2811556Srgrimes}
2821556Srgrimes
2831556Srgrimes
2841556Srgrimes
2851556Srgrimes/*
2861556Srgrimes * Read a file containing shell functions.
2871556Srgrimes */
2881556Srgrimes
2891556Srgrimesvoid
29090111Simpreadcmdfile(char *name)
29117987Speter{
2921556Srgrimes	int fd;
2931556Srgrimes
2941556Srgrimes	INTOFF;
2951556Srgrimes	if ((fd = open(name, O_RDONLY)) >= 0)
2961556Srgrimes		setinputfd(fd, 1);
2971556Srgrimes	else
29853891Scracauer		error("Can't open %s: %s", name, strerror(errno));
2991556Srgrimes	INTON;
3001556Srgrimes	cmdloop(0);
3011556Srgrimes	popfile();
3021556Srgrimes}
3031556Srgrimes
3041556Srgrimes
3051556Srgrimes
3061556Srgrimes/*
30746684Skris * Take commands from a file.  To be compatible we should do a path
30817987Speter * search for the file, which is necessary to find sub-commands.
3091556Srgrimes */
3101556Srgrimes
31117987Speter
31217987SpeterSTATIC char *
31390111Simpfind_dot_file(char *basename)
31417987Speter{
31517987Speter	static char localname[FILENAME_MAX+1];
31617987Speter	char *fullname;
31717987Speter	char *path = pathval();
31817987Speter	struct stat statb;
31917987Speter
32017987Speter	/* don't try this for absolute or relative paths */
32117987Speter	if( strchr(basename, '/'))
32217987Speter		return basename;
32317987Speter
32417987Speter	while ((fullname = padvance(&path, basename)) != NULL) {
32517987Speter		strcpy(localname, fullname);
32617987Speter		stunalloc(fullname);
32717987Speter		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode))
32817987Speter			return localname;
32917987Speter	}
33017987Speter	return basename;
33117987Speter}
33217987Speter
33317987Speterint
33490111Simpdotcmd(int argc, char **argv)
33517987Speter{
33617987Speter	struct strlist *sp;
3371556Srgrimes	exitstatus = 0;
33817987Speter
33917987Speter	for (sp = cmdenviron; sp ; sp = sp->next)
34017987Speter		setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED);
34117987Speter
3421556Srgrimes	if (argc >= 2) {		/* That's what SVR2 does */
34317987Speter		char *fullname = find_dot_file(argv[1]);
34417987Speter
34517987Speter		setinputfile(fullname, 1);
34617987Speter		commandname = fullname;
3471556Srgrimes		cmdloop(0);
3481556Srgrimes		popfile();
3491556Srgrimes	}
3501556Srgrimes	return exitstatus;
3511556Srgrimes}
3521556Srgrimes
3531556Srgrimes
35417987Speterint
35590111Simpexitcmd(int argc, char **argv)
35617987Speter{
35718267Sadam	extern int oexitstatus;
35818267Sadam
3591556Srgrimes	if (stoppedjobs())
36017987Speter		return 0;
36120425Ssteve	if (argc > 1)
36220425Ssteve		exitstatus = number(argv[1]);
36320425Ssteve	else
36420425Ssteve		exitstatus = oexitstatus;
36518254Sbde	exitshell(exitstatus);
36617987Speter	/*NOTREACHED*/
36717987Speter	return 0;
3681556Srgrimes}
369