main.c revision 104261
10SN/A/*-
215774Sclanger * Copyright (c) 1991, 1993
30SN/A *	The Regents of the University of California.  All rights reserved.
40SN/A *
50SN/A * This code is derived from software contributed to Berkeley by
60SN/A * Kenneth Almquist.
72362SN/A *
80SN/A * Redistribution and use in source and binary forms, with or without
92362SN/A * modification, are permitted provided that the following conditions
100SN/A * are met:
110SN/A * 1. Redistributions of source code must retain the above copyright
120SN/A *    notice, this list of conditions and the following disclaimer.
130SN/A * 2. Redistributions in binary form must reproduce the above copyright
140SN/A *    notice, this list of conditions and the following disclaimer in the
150SN/A *    documentation and/or other materials provided with the distribution.
160SN/A * 3. All advertising materials mentioning features or use of this software
170SN/A *    must display the following acknowledgement:
180SN/A *	This product includes software developed by the University of
190SN/A *	California, Berkeley and its contributors.
200SN/A * 4. Neither the name of the University nor the names of its contributors
212362SN/A *    may be used to endorse or promote products derived from this software
222362SN/A *    without specific prior written permission.
232362SN/A *
240SN/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2515918Sclanger * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2615918Sclanger * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
270SN/A * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
280SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
290SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
300SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
310SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
320SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
330SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
340SN/A * SUCH DAMAGE.
350SN/A */
360SN/A
370SN/A#ifndef lint
380SN/Astatic char const copyright[] =
390SN/A"@(#) Copyright (c) 1991, 1993\n\
400SN/A	The Regents of the University of California.  All rights reserved.\n";
410SN/A#endif /* not lint */
420SN/A
430SN/A#ifndef lint
440SN/A#if 0
450SN/Astatic char sccsid[] = "@(#)main.c	8.6 (Berkeley) 5/28/95";
460SN/A#endif
470SN/A#endif /* not lint */
480SN/A#include <sys/cdefs.h>
490SN/A__FBSDID("$FreeBSD: head/bin/sh/main.c 104261 2002-10-01 01:30:33Z tjr $");
500SN/A
510SN/A#include <stdio.h>
520SN/A#include <signal.h>
530SN/A#include <sys/stat.h>
540SN/A#include <unistd.h>
550SN/A#include <fcntl.h>
560SN/A#include <locale.h>
570SN/A#include <errno.h>
580SN/A
590SN/A#include "shell.h"
600SN/A#include "main.h"
610SN/A#include "mail.h"
620SN/A#include "options.h"
630SN/A#include "output.h"
640SN/A#include "parser.h"
650SN/A#include "nodes.h"
660SN/A#include "expand.h"
670SN/A#include "eval.h"
680SN/A#include "jobs.h"
690SN/A#include "input.h"
700SN/A#include "trap.h"
710SN/A#include "var.h"
720SN/A#include "show.h"
730SN/A#include "memalloc.h"
740SN/A#include "error.h"
750SN/A#include "init.h"
760SN/A#include "mystring.h"
770SN/A#include "exec.h"
780SN/A#include "cd.h"
790SN/A
800SN/Aint rootpid;
810SN/Aint rootshell;
820SN/A
830SN/ASTATIC void read_profile(char *);
840SN/ASTATIC char *find_dot_file(char *);
850SN/A
860SN/A/*
870SN/A * Main routine.  We initialize things, parse the arguments, execute
880SN/A * profiles if we're a login shell, and then call cmdloop to execute
890SN/A * commands.  The setjmp call sets up the location to jump to when an
900SN/A * exception occurs.  When an exception occurs the variable "state"
910SN/A * is used to figure out how far we had gotten.
920SN/A */
930SN/A
940SN/Aint
950SN/Amain(int argc, char *argv[])
960SN/A{
970SN/A	struct jmploc jmploc;
980SN/A	struct stackmark smark;
990SN/A	volatile int state;
1000SN/A	char *shinit;
1010SN/A
1020SN/A	(void) setlocale(LC_ALL, "");
1030SN/A	state = 0;
1040SN/A	if (setjmp(jmploc.loc)) {
1050SN/A		/*
1060SN/A		 * When a shell procedure is executed, we raise the
1070SN/A		 * exception EXSHELLPROC to clean up before executing
1080SN/A		 * the shell procedure.
1090SN/A		 */
1107041SN/A		switch (exception) {
1117041SN/A		case EXSHELLPROC:
1127041SN/A			rootpid = getpid();
1133877SN/A			rootshell = 1;
1140SN/A			minusc = NULL;
1157041SN/A			state = 3;
1167041SN/A			break;
1177041SN/A
1187041SN/A		case EXEXEC:
1197041SN/A			exitstatus = exerrno;
1207041SN/A			break;
1217041SN/A
1223877SN/A		case EXERROR:
1230SN/A			exitstatus = 2;
1240SN/A			break;
1250SN/A
1267041SN/A		default:
1270SN/A			break;
1280SN/A		}
1290SN/A
1307041SN/A		if (exception != EXSHELLPROC) {
1317041SN/A		    if (state == 0 || iflag == 0 || ! rootshell)
1327041SN/A			    exitshell(exitstatus);
1337041SN/A		}
1347041SN/A		reset();
1357041SN/A		if (exception == EXINT) {
1363877SN/A			out2c('\n');
1370SN/A			flushout(&errout);
1380SN/A		}
1390SN/A		popstackmark(&smark);
1400SN/A		FORCEINTON;				/* enable interrupts */
1417041SN/A		if (state == 1)
1427041SN/A			goto state1;
1437041SN/A		else if (state == 2)
1447041SN/A			goto state2;
1457041SN/A		else if (state == 3)
1467041SN/A			goto state3;
1477041SN/A		else
1480SN/A			goto state4;
1490SN/A	}
1500SN/A	handler = &jmploc;
1510SN/A#ifdef DEBUG
1520SN/A	opentrace();
1530SN/A	trputs("Shell args:  ");  trargs(argv);
1540SN/A#endif
1550SN/A	rootpid = getpid();
1560SN/A	rootshell = 1;
1570SN/A	init();
1580SN/A	setstackmark(&smark);
1590SN/A	procargs(argc, argv);
1600SN/A	if (getpwd() == NULL && iflag)
1610SN/A		out2str("sh: cannot determine working directory\n");
1620SN/A	if (argv[0] && argv[0][0] == '-') {
1630SN/A		state = 1;
1640SN/A		read_profile("/etc/profile");
1650SN/Astate1:
1660SN/A		state = 2;
1673877SN/A		if (privileged == 0)
1680SN/A			read_profile(".profile");
1690SN/A		else
1700SN/A			read_profile("/etc/suid_profile");
1710SN/A	}
1720SN/Astate2:
1730SN/A	state = 3;
1740SN/A	if (!privileged && iflag) {
1750SN/A		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
1766234SN/A			state = 3;
1770SN/A			read_profile(shinit);
1780SN/A		}
1790SN/A	}
1800SN/Astate3:
1810SN/A	state = 4;
1820SN/A	if (minusc) {
1837041SN/A		evalstring(minusc);
1847041SN/A	}
1857041SN/A	if (sflag || minusc == NULL) {
1867041SN/Astate4:	/* XXX ??? - why isn't this before the "if" statement */
1877041SN/A		cmdloop(1);
1883877SN/A	}
1890SN/A	exitshell(exitstatus);
1907041SN/A	/*NOTREACHED*/
1917041SN/A	return 0;
1927041SN/A}
1937041SN/A
1947041SN/A
1957041SN/A/*
1967041SN/A * Read and execute commands.  "Top" is nonzero for the top level command
1973877SN/A * loop; it turns on prompting if the shell is interactive.
1980SN/A */
1990SN/A
2000SN/Avoid
2017041SN/Acmdloop(int top)
2020SN/A{
2030SN/A	union node *n;
2040SN/A	struct stackmark smark;
2050SN/A	int inter;
2060SN/A	int numeof = 0;
2070SN/A
2080SN/A	TRACE(("cmdloop(%d) called\n", top));
2090SN/A	setstackmark(&smark);
2100SN/A	for (;;) {
2110SN/A		if (pendingsigs)
2120SN/A			dotrap();
2130SN/A		inter = 0;
2140SN/A		if (iflag && top) {
2150SN/A			inter++;
2160SN/A			showjobs(1, 0, 0);
2170SN/A			chkmail(0);
2180SN/A			flushout(&output);
2190SN/A		}
2200SN/A		n = parsecmd(inter);
2210SN/A		/* showtree(n); DEBUG */
2220SN/A		if (n == NEOF) {
2230SN/A			if (!top || numeof >= 50)
2240SN/A				break;
2250SN/A			if (!stoppedjobs()) {
2265319SN/A				if (!Iflag)
2270SN/A					break;
2280SN/A				out2str("\nUse \"exit\" to leave shell.\n");
2290SN/A			}
2305319SN/A			numeof++;
2310SN/A		} else if (n != NULL && nflag == 0) {
2320SN/A			job_warning = (job_warning == 2) ? 1 : 0;
2330SN/A			numeof = 0;
2345319SN/A			evaltree(n, 0);
2350SN/A		}
2360SN/A		popstackmark(&smark);
2370SN/A		setstackmark(&smark);
2380SN/A		if (evalskip == SKIPFILE) {
2390SN/A			evalskip = 0;
2400SN/A			break;
2410SN/A		}
2425319SN/A	}
2430SN/A	popstackmark(&smark);
2440SN/A}
2450SN/A
2460SN/A
2475319SN/A
2480SN/A/*
2490SN/A * Read /etc/profile or .profile.  Return on error.
2500SN/A */
2515319SN/A
2525319SN/ASTATIC void
2535319SN/Aread_profile(char *name)
2545319SN/A{
2555319SN/A	int fd;
2560SN/A
2570SN/A	INTOFF;
2580SN/A	if ((fd = open(name, O_RDONLY)) >= 0)
2595319SN/A		setinputfd(fd, 1);
2600SN/A	INTON;
2610SN/A	if (fd < 0)
2620SN/A		return;
2630SN/A	cmdloop(0);
2640SN/A	popfile();
2650SN/A}
2660SN/A
2670SN/A
2686234SN/A
2696234SN/A/*
2706234SN/A * Read a file containing shell functions.
2716234SN/A */
2726234SN/A
2736234SN/Avoid
2746234SN/Areadcmdfile(char *name)
2756234SN/A{
2766234SN/A	int fd;
2776234SN/A
2780SN/A	INTOFF;
2790SN/A	if ((fd = open(name, O_RDONLY)) >= 0)
2800SN/A		setinputfd(fd, 1);
2810SN/A	else
2820SN/A		error("Can't open %s: %s", name, strerror(errno));
2830SN/A	INTON;
2840SN/A	cmdloop(0);
2850SN/A	popfile();
2860SN/A}
2875618SN/A
2880SN/A
2890SN/A
2900SN/A/*
2910SN/A * Take commands from a file.  To be compatible we should do a path
2920SN/A * search for the file, which is necessary to find sub-commands.
2930SN/A */
2940SN/A
2950SN/A
2960SN/ASTATIC char *
2970SN/Afind_dot_file(char *basename)
2980SN/A{
2996234SN/A	static char localname[FILENAME_MAX+1];
3006234SN/A	char *fullname;
3016234SN/A	char *path = pathval();
3026234SN/A	struct stat statb;
3036234SN/A
3046234SN/A	/* don't try this for absolute or relative paths */
3056234SN/A	if( strchr(basename, '/'))
3066234SN/A		return basename;
3076234SN/A
3086234SN/A	while ((fullname = padvance(&path, basename)) != NULL) {
3096234SN/A		strcpy(localname, fullname);
3106234SN/A		stunalloc(fullname);
3116234SN/A		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode))
3126234SN/A			return localname;
3136234SN/A	}
3146234SN/A	return basename;
3156234SN/A}
3166234SN/A
3176234SN/Aint
3186234SN/Adotcmd(int argc, char **argv)
3196234SN/A{
3206234SN/A	struct strlist *sp;
3216234SN/A	exitstatus = 0;
3220SN/A
3230SN/A	for (sp = cmdenviron; sp ; sp = sp->next)
3243877SN/A		setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED);
3250SN/A
3260SN/A	if (argc >= 2) {		/* That's what SVR2 does */
3270SN/A		char *fullname = find_dot_file(argv[1]);
3280SN/A
3290SN/A		setinputfile(fullname, 1);
3300SN/A		commandname = fullname;
3310SN/A		cmdloop(0);
3320SN/A		popfile();
3330SN/A	}
3340SN/A	return exitstatus;
3350SN/A}
3360SN/A
3370SN/A
3380SN/Aint
3390SN/Aexitcmd(int argc, char **argv)
3400SN/A{
3410SN/A	extern int oexitstatus;
3420SN/A
3430SN/A	if (stoppedjobs())
3440SN/A		return 0;
3450SN/A	if (argc > 1)
3460SN/A		exitstatus = number(argv[1]);
3470SN/A	else
3480SN/A		exitstatus = oexitstatus;
3490SN/A	exitshell(exitstatus);
3500SN/A	/*NOTREACHED*/
3510SN/A	return 0;
3520SN/A}
3530SN/A