main.c revision 163085
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 * 4. Neither the name of the University nor the names of its contributors
171556Srgrimes *    may be used to endorse or promote products derived from this software
181556Srgrimes *    without specific prior written permission.
191556Srgrimes *
201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301556Srgrimes * SUCH DAMAGE.
311556Srgrimes */
321556Srgrimes
331556Srgrimes#ifndef lint
3420425Sstevestatic char const copyright[] =
351556Srgrimes"@(#) Copyright (c) 1991, 1993\n\
361556Srgrimes	The Regents of the University of California.  All rights reserved.\n";
371556Srgrimes#endif /* not lint */
381556Srgrimes
391556Srgrimes#ifndef lint
4036150Scharnier#if 0
4136150Scharnierstatic char sccsid[] = "@(#)main.c	8.6 (Berkeley) 5/28/95";
4236150Scharnier#endif
431556Srgrimes#endif /* not lint */
4499110Sobrien#include <sys/cdefs.h>
4599110Sobrien__FBSDID("$FreeBSD: head/bin/sh/main.c 163085 2006-10-07 16:51:16Z stefanf $");
461556Srgrimes
4717987Speter#include <stdio.h>
481556Srgrimes#include <signal.h>
4917987Speter#include <sys/stat.h>
5017987Speter#include <unistd.h>
511556Srgrimes#include <fcntl.h>
5217525Sache#include <locale.h>
5359214Simp#include <errno.h>
5417525Sache
551556Srgrimes#include "shell.h"
561556Srgrimes#include "main.h"
571556Srgrimes#include "mail.h"
581556Srgrimes#include "options.h"
591556Srgrimes#include "output.h"
601556Srgrimes#include "parser.h"
611556Srgrimes#include "nodes.h"
6217987Speter#include "expand.h"
631556Srgrimes#include "eval.h"
641556Srgrimes#include "jobs.h"
651556Srgrimes#include "input.h"
661556Srgrimes#include "trap.h"
671556Srgrimes#include "var.h"
6817987Speter#include "show.h"
691556Srgrimes#include "memalloc.h"
701556Srgrimes#include "error.h"
711556Srgrimes#include "init.h"
721556Srgrimes#include "mystring.h"
7317987Speter#include "exec.h"
7420425Ssteve#include "cd.h"
751556Srgrimes
761556Srgrimesint rootpid;
771556Srgrimesint rootshell;
781556Srgrimes
7990111SimpSTATIC void read_profile(char *);
8090111SimpSTATIC char *find_dot_file(char *);
811556Srgrimes
821556Srgrimes/*
831556Srgrimes * Main routine.  We initialize things, parse the arguments, execute
841556Srgrimes * profiles if we're a login shell, and then call cmdloop to execute
851556Srgrimes * commands.  The setjmp call sets up the location to jump to when an
861556Srgrimes * exception occurs.  When an exception occurs the variable "state"
871556Srgrimes * is used to figure out how far we had gotten.
881556Srgrimes */
891556Srgrimes
9017987Speterint
9190111Simpmain(int argc, char *argv[])
9217987Speter{
931556Srgrimes	struct jmploc jmploc;
941556Srgrimes	struct stackmark smark;
951556Srgrimes	volatile int state;
961556Srgrimes	char *shinit;
971556Srgrimes
9817525Sache	(void) setlocale(LC_ALL, "");
991556Srgrimes	state = 0;
1001556Srgrimes	if (setjmp(jmploc.loc)) {
1011556Srgrimes		/*
1021556Srgrimes		 * When a shell procedure is executed, we raise the
1031556Srgrimes		 * exception EXSHELLPROC to clean up before executing
1041556Srgrimes		 * the shell procedure.
1051556Srgrimes		 */
10620425Ssteve		switch (exception) {
10720425Ssteve		case EXSHELLPROC:
1081556Srgrimes			rootpid = getpid();
1091556Srgrimes			rootshell = 1;
1101556Srgrimes			minusc = NULL;
1111556Srgrimes			state = 3;
11220425Ssteve			break;
11320425Ssteve
11420425Ssteve		case EXEXEC:
11520425Ssteve			exitstatus = exerrno;
11620425Ssteve			break;
11720425Ssteve
11820425Ssteve		case EXERROR:
11920425Ssteve			exitstatus = 2;
12020425Ssteve			break;
12120425Ssteve
12220425Ssteve		default:
12320425Ssteve			break;
12420425Ssteve		}
12520425Ssteve
12620425Ssteve		if (exception != EXSHELLPROC) {
12720425Ssteve		    if (state == 0 || iflag == 0 || ! rootshell)
12820425Ssteve			    exitshell(exitstatus);
12920425Ssteve		}
1301556Srgrimes		reset();
131104255Stjr		if (exception == EXINT) {
1321556Srgrimes			out2c('\n');
1331556Srgrimes			flushout(&errout);
1341556Srgrimes		}
1351556Srgrimes		popstackmark(&smark);
1361556Srgrimes		FORCEINTON;				/* enable interrupts */
1371556Srgrimes		if (state == 1)
1381556Srgrimes			goto state1;
1391556Srgrimes		else if (state == 2)
1401556Srgrimes			goto state2;
1411556Srgrimes		else if (state == 3)
1421556Srgrimes			goto state3;
1431556Srgrimes		else
1441556Srgrimes			goto state4;
1451556Srgrimes	}
1461556Srgrimes	handler = &jmploc;
1471556Srgrimes#ifdef DEBUG
1481556Srgrimes	opentrace();
1491556Srgrimes	trputs("Shell args:  ");  trargs(argv);
1501556Srgrimes#endif
1511556Srgrimes	rootpid = getpid();
1521556Srgrimes	rootshell = 1;
1531556Srgrimes	init();
1541556Srgrimes	setstackmark(&smark);
1551556Srgrimes	procargs(argc, argv);
15620774Ssteve	if (getpwd() == NULL && iflag)
15720774Ssteve		out2str("sh: cannot determine working directory\n");
158155304Sschweikh	if (getpwd() != NULL)
159155304Sschweikh		setvar ("PWD", getpwd(), VEXPORT);
1601556Srgrimes	if (argv[0] && argv[0][0] == '-') {
1611556Srgrimes		state = 1;
1621556Srgrimes		read_profile("/etc/profile");
1631556Srgrimesstate1:
1641556Srgrimes		state = 2;
16519240Ssteve		if (privileged == 0)
16619240Ssteve			read_profile(".profile");
16719240Ssteve		else
16819240Ssteve			read_profile("/etc/suid_profile");
1698855Srgrimes	}
1701556Srgrimesstate2:
1711556Srgrimes	state = 3;
17225471Ssteve	if (!privileged && iflag) {
17317987Speter		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
17417987Speter			state = 3;
17517987Speter			read_profile(shinit);
17617987Speter		}
1771556Srgrimes	}
1781556Srgrimesstate3:
1791556Srgrimes	state = 4;
1801556Srgrimes	if (minusc) {
1811556Srgrimes		evalstring(minusc);
1821556Srgrimes	}
1831556Srgrimes	if (sflag || minusc == NULL) {
1841556Srgrimesstate4:	/* XXX ??? - why isn't this before the "if" statement */
1851556Srgrimes		cmdloop(1);
1861556Srgrimes	}
1871556Srgrimes	exitshell(exitstatus);
18817987Speter	/*NOTREACHED*/
18917987Speter	return 0;
1901556Srgrimes}
1911556Srgrimes
1921556Srgrimes
1931556Srgrimes/*
1941556Srgrimes * Read and execute commands.  "Top" is nonzero for the top level command
1951556Srgrimes * loop; it turns on prompting if the shell is interactive.
1961556Srgrimes */
1971556Srgrimes
1981556Srgrimesvoid
19990111Simpcmdloop(int top)
20017987Speter{
2011556Srgrimes	union node *n;
2021556Srgrimes	struct stackmark smark;
2031556Srgrimes	int inter;
2041556Srgrimes	int numeof = 0;
2051556Srgrimes
2061556Srgrimes	TRACE(("cmdloop(%d) called\n", top));
2071556Srgrimes	setstackmark(&smark);
2081556Srgrimes	for (;;) {
2091556Srgrimes		if (pendingsigs)
2101556Srgrimes			dotrap();
2111556Srgrimes		inter = 0;
2121556Srgrimes		if (iflag && top) {
2131556Srgrimes			inter++;
214163085Sstefanf			showjobs(1, SHOWJOBS_DEFAULT);
2151556Srgrimes			chkmail(0);
2161556Srgrimes			flushout(&output);
2171556Srgrimes		}
2181556Srgrimes		n = parsecmd(inter);
2191556Srgrimes		/* showtree(n); DEBUG */
2201556Srgrimes		if (n == NEOF) {
2211556Srgrimes			if (!top || numeof >= 50)
2221556Srgrimes				break;
2231556Srgrimes			if (!stoppedjobs()) {
2241556Srgrimes				if (!Iflag)
2251556Srgrimes					break;
2261556Srgrimes				out2str("\nUse \"exit\" to leave shell.\n");
2271556Srgrimes			}
2281556Srgrimes			numeof++;
2291556Srgrimes		} else if (n != NULL && nflag == 0) {
2301556Srgrimes			job_warning = (job_warning == 2) ? 1 : 0;
2311556Srgrimes			numeof = 0;
2321556Srgrimes			evaltree(n, 0);
2331556Srgrimes		}
2341556Srgrimes		popstackmark(&smark);
23564702Scracauer		setstackmark(&smark);
23620425Ssteve		if (evalskip == SKIPFILE) {
23720425Ssteve			evalskip = 0;
23820425Ssteve			break;
23920425Ssteve		}
2401556Srgrimes	}
24164702Scracauer	popstackmark(&smark);
2421556Srgrimes}
2431556Srgrimes
2441556Srgrimes
2451556Srgrimes
2461556Srgrimes/*
2471556Srgrimes * Read /etc/profile or .profile.  Return on error.
2481556Srgrimes */
2491556Srgrimes
2501556SrgrimesSTATIC void
25190111Simpread_profile(char *name)
25290111Simp{
2531556Srgrimes	int fd;
2541556Srgrimes
2551556Srgrimes	INTOFF;
2561556Srgrimes	if ((fd = open(name, O_RDONLY)) >= 0)
2571556Srgrimes		setinputfd(fd, 1);
2581556Srgrimes	INTON;
2591556Srgrimes	if (fd < 0)
2601556Srgrimes		return;
2611556Srgrimes	cmdloop(0);
2621556Srgrimes	popfile();
2631556Srgrimes}
2641556Srgrimes
2651556Srgrimes
2661556Srgrimes
2671556Srgrimes/*
2681556Srgrimes * Read a file containing shell functions.
2691556Srgrimes */
2701556Srgrimes
2711556Srgrimesvoid
27290111Simpreadcmdfile(char *name)
27317987Speter{
2741556Srgrimes	int fd;
2751556Srgrimes
2761556Srgrimes	INTOFF;
2771556Srgrimes	if ((fd = open(name, O_RDONLY)) >= 0)
2781556Srgrimes		setinputfd(fd, 1);
2791556Srgrimes	else
28053891Scracauer		error("Can't open %s: %s", name, strerror(errno));
2811556Srgrimes	INTON;
2821556Srgrimes	cmdloop(0);
2831556Srgrimes	popfile();
2841556Srgrimes}
2851556Srgrimes
2861556Srgrimes
2871556Srgrimes
2881556Srgrimes/*
28946684Skris * Take commands from a file.  To be compatible we should do a path
29017987Speter * search for the file, which is necessary to find sub-commands.
2911556Srgrimes */
2921556Srgrimes
29317987Speter
29417987SpeterSTATIC char *
29590111Simpfind_dot_file(char *basename)
29617987Speter{
29717987Speter	static char localname[FILENAME_MAX+1];
29817987Speter	char *fullname;
29917987Speter	char *path = pathval();
30017987Speter	struct stat statb;
30117987Speter
30217987Speter	/* don't try this for absolute or relative paths */
30317987Speter	if( strchr(basename, '/'))
30417987Speter		return basename;
30517987Speter
30617987Speter	while ((fullname = padvance(&path, basename)) != NULL) {
30717987Speter		strcpy(localname, fullname);
30817987Speter		stunalloc(fullname);
30917987Speter		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode))
31017987Speter			return localname;
31117987Speter	}
31217987Speter	return basename;
31317987Speter}
31417987Speter
31517987Speterint
31690111Simpdotcmd(int argc, char **argv)
31717987Speter{
31817987Speter	struct strlist *sp;
319157414Sstefanf	char *fullname;
320157414Sstefanf
321157414Sstefanf	if (argc < 2)
322157414Sstefanf		error("missing filename");
323157414Sstefanf
3241556Srgrimes	exitstatus = 0;
32517987Speter
32617987Speter	for (sp = cmdenviron; sp ; sp = sp->next)
32717987Speter		setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED);
32817987Speter
329157414Sstefanf	fullname = find_dot_file(argv[1]);
330157414Sstefanf	setinputfile(fullname, 1);
331157414Sstefanf	commandname = fullname;
332157414Sstefanf	cmdloop(0);
333157414Sstefanf	popfile();
3341556Srgrimes	return exitstatus;
3351556Srgrimes}
3361556Srgrimes
3371556Srgrimes
33817987Speterint
33990111Simpexitcmd(int argc, char **argv)
34017987Speter{
34118267Sadam	extern int oexitstatus;
34218267Sadam
3431556Srgrimes	if (stoppedjobs())
34417987Speter		return 0;
34520425Ssteve	if (argc > 1)
34620425Ssteve		exitstatus = number(argv[1]);
34720425Ssteve	else
34820425Ssteve		exitstatus = oexitstatus;
34918254Sbde	exitshell(exitstatus);
35017987Speter	/*NOTREACHED*/
35117987Speter	return 0;
3521556Srgrimes}
353