main.c revision 99110
1189251Ssam/*-
2189251Ssam * Copyright (c) 1991, 1993
3189251Ssam *	The Regents of the University of California.  All rights reserved.
4189251Ssam *
5189251Ssam * This code is derived from software contributed to Berkeley by
6189251Ssam * Kenneth Almquist.
7189251Ssam *
8189251Ssam * Redistribution and use in source and binary forms, with or without
9189251Ssam * modification, are permitted provided that the following conditions
10189251Ssam * are met:
11189251Ssam * 1. Redistributions of source code must retain the above copyright
12189251Ssam *    notice, this list of conditions and the following disclaimer.
13189251Ssam * 2. Redistributions in binary form must reproduce the above copyright
14189251Ssam *    notice, this list of conditions and the following disclaimer in the
15189251Ssam *    documentation and/or other materials provided with the distribution.
16189251Ssam * 3. All advertising materials mentioning features or use of this software
17189251Ssam *    must display the following acknowledgement:
18189251Ssam *	This product includes software developed by the University of
19189251Ssam *	California, Berkeley and its contributors.
20189251Ssam * 4. Neither the name of the University nor the names of its contributors
21189251Ssam *    may be used to endorse or promote products derived from this software
22189251Ssam *    without specific prior written permission.
23189251Ssam *
24189251Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25189251Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27189251Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28189251Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29189251Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30189251Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31189251Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32189251Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33189251Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34189251Ssam * SUCH DAMAGE.
35189251Ssam */
36189251Ssam
37189251Ssam#ifndef lint
38189251Ssamstatic char const copyright[] =
39189251Ssam"@(#) Copyright (c) 1991, 1993\n\
40189251Ssam	The Regents of the University of California.  All rights reserved.\n";
41189251Ssam#endif /* not lint */
42189251Ssam
43189251Ssam#ifndef lint
44189251Ssam#if 0
45189251Ssamstatic char sccsid[] = "@(#)main.c	8.6 (Berkeley) 5/28/95";
46189251Ssam#endif
47189251Ssam#endif /* not lint */
48189251Ssam#include <sys/cdefs.h>
49189251Ssam__FBSDID("$FreeBSD: head/bin/sh/main.c 99110 2002-06-30 05:15:05Z obrien $");
50189251Ssam
51189251Ssam#include <stdio.h>
52189251Ssam#include <signal.h>
53189251Ssam#include <sys/stat.h>
54189251Ssam#include <unistd.h>
55189251Ssam#include <fcntl.h>
56189251Ssam#include <locale.h>
57189251Ssam#include <errno.h>
58189251Ssam
59189251Ssam#include "shell.h"
60189251Ssam#include "main.h"
61189251Ssam#include "mail.h"
62189251Ssam#include "options.h"
63189251Ssam#include "output.h"
64189251Ssam#include "parser.h"
65189251Ssam#include "nodes.h"
66189251Ssam#include "expand.h"
67189251Ssam#include "eval.h"
68189251Ssam#include "jobs.h"
69189251Ssam#include "input.h"
70189251Ssam#include "trap.h"
71189251Ssam#include "var.h"
72189251Ssam#include "show.h"
73189251Ssam#include "memalloc.h"
74189251Ssam#include "error.h"
75189251Ssam#include "init.h"
76189251Ssam#include "mystring.h"
77189251Ssam#include "exec.h"
78189251Ssam#include "cd.h"
79189251Ssam
80189251Ssam#define PROFILE 0
81189251Ssam
82189251Ssamint rootpid;
83189251Ssamint rootshell;
84189251Ssam#if PROFILE
85189251Ssamshort profile_buf[16384];
86189251Ssamextern int etext();
87189251Ssam#endif
88189251Ssam
89189251SsamSTATIC void read_profile(char *);
90189251SsamSTATIC char *find_dot_file(char *);
91189251Ssam
92189251Ssam/*
93189251Ssam * Main routine.  We initialize things, parse the arguments, execute
94189251Ssam * profiles if we're a login shell, and then call cmdloop to execute
95189251Ssam * commands.  The setjmp call sets up the location to jump to when an
96189251Ssam * exception occurs.  When an exception occurs the variable "state"
97189251Ssam * is used to figure out how far we had gotten.
98189251Ssam */
99189251Ssam
100189251Ssamint
101189251Ssammain(int argc, char *argv[])
102189251Ssam{
103189251Ssam	struct jmploc jmploc;
104189251Ssam	struct stackmark smark;
105189251Ssam	volatile int state;
106189251Ssam	char *shinit;
107189251Ssam
108189251Ssam#if PROFILE
109189251Ssam	monitor(4, etext, profile_buf, sizeof profile_buf, 50);
110189251Ssam#endif
111189251Ssam	(void) setlocale(LC_ALL, "");
112189251Ssam	state = 0;
113189251Ssam	if (setjmp(jmploc.loc)) {
114189251Ssam		/*
115189251Ssam		 * When a shell procedure is executed, we raise the
116189251Ssam		 * exception EXSHELLPROC to clean up before executing
117189251Ssam		 * the shell procedure.
118189251Ssam		 */
119189251Ssam		switch (exception) {
120189251Ssam		case EXSHELLPROC:
121189251Ssam			rootpid = getpid();
122189251Ssam			rootshell = 1;
123189251Ssam			minusc = NULL;
124189251Ssam			state = 3;
125189251Ssam			break;
126189251Ssam
127189251Ssam		case EXEXEC:
128189251Ssam			exitstatus = exerrno;
129189251Ssam			break;
130189251Ssam
131189251Ssam		case EXERROR:
132189251Ssam			exitstatus = 2;
133189251Ssam			break;
134189251Ssam
135189251Ssam		default:
136189251Ssam			break;
137189251Ssam		}
138189251Ssam
139189251Ssam		if (exception != EXSHELLPROC) {
140189251Ssam		    if (state == 0 || iflag == 0 || ! rootshell)
141189251Ssam			    exitshell(exitstatus);
142189251Ssam		}
143189251Ssam		reset();
144189251Ssam		if (exception == EXINT
145189251Ssam#if ATTY
146189251Ssam		 && (! attyset() || equal(termval(), "emacs"))
147189251Ssam#endif
148189251Ssam		 ) {
149189251Ssam			out2c('\n');
150189251Ssam			flushout(&errout);
151189251Ssam		}
152189251Ssam		popstackmark(&smark);
153189251Ssam		FORCEINTON;				/* enable interrupts */
154189251Ssam		if (state == 1)
155189251Ssam			goto state1;
156189251Ssam		else if (state == 2)
157189251Ssam			goto state2;
158189251Ssam		else if (state == 3)
159189251Ssam			goto state3;
160189251Ssam		else
161189251Ssam			goto state4;
162189251Ssam	}
163189251Ssam	handler = &jmploc;
164189251Ssam#ifdef DEBUG
165189251Ssam	opentrace();
166189251Ssam	trputs("Shell args:  ");  trargs(argv);
167189251Ssam#endif
168189251Ssam	rootpid = getpid();
169189251Ssam	rootshell = 1;
170189251Ssam	init();
171189251Ssam	setstackmark(&smark);
172189251Ssam	procargs(argc, argv);
173189251Ssam	if (getpwd() == NULL && iflag)
174189251Ssam		out2str("sh: cannot determine working directory\n");
175189251Ssam	if (argv[0] && argv[0][0] == '-') {
176189251Ssam		state = 1;
177189251Ssam		read_profile("/etc/profile");
178189251Ssamstate1:
179189251Ssam		state = 2;
180189251Ssam		if (privileged == 0)
181189251Ssam			read_profile(".profile");
182189251Ssam		else
183189251Ssam			read_profile("/etc/suid_profile");
184189251Ssam	}
185189251Ssamstate2:
186189251Ssam	state = 3;
187189251Ssam	if (!privileged && iflag) {
188189251Ssam		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
189189251Ssam			state = 3;
190189251Ssam			read_profile(shinit);
191189251Ssam		}
192189251Ssam	}
193189251Ssamstate3:
194189251Ssam	state = 4;
195189251Ssam	if (minusc) {
196189251Ssam		evalstring(minusc);
197189251Ssam	}
198189251Ssam	if (sflag || minusc == NULL) {
199189251Ssamstate4:	/* XXX ??? - why isn't this before the "if" statement */
200189251Ssam		cmdloop(1);
201189251Ssam	}
202189251Ssam#if PROFILE
203189251Ssam	monitor(0);
204189251Ssam#endif
205189251Ssam	exitshell(exitstatus);
206189251Ssam	/*NOTREACHED*/
207189251Ssam	return 0;
208189251Ssam}
209189251Ssam
210189251Ssam
211189251Ssam/*
212189251Ssam * Read and execute commands.  "Top" is nonzero for the top level command
213189251Ssam * loop; it turns on prompting if the shell is interactive.
214189251Ssam */
215189251Ssam
216189251Ssamvoid
217189251Ssamcmdloop(int top)
218189251Ssam{
219189251Ssam	union node *n;
220189251Ssam	struct stackmark smark;
221189251Ssam	int inter;
222189251Ssam	int numeof = 0;
223189251Ssam
224189251Ssam	TRACE(("cmdloop(%d) called\n", top));
225189251Ssam	setstackmark(&smark);
226189251Ssam	for (;;) {
227189251Ssam		if (pendingsigs)
228189251Ssam			dotrap();
229189251Ssam		inter = 0;
230189251Ssam		if (iflag && top) {
231189251Ssam			inter++;
232189251Ssam			showjobs(1, 0, 0);
233189251Ssam			chkmail(0);
234189251Ssam			flushout(&output);
235189251Ssam		}
236189251Ssam		n = parsecmd(inter);
237189251Ssam		/* showtree(n); DEBUG */
238189251Ssam		if (n == NEOF) {
239189251Ssam			if (!top || numeof >= 50)
240189251Ssam				break;
241189251Ssam			if (!stoppedjobs()) {
242189251Ssam				if (!Iflag)
243189251Ssam					break;
244189251Ssam				out2str("\nUse \"exit\" to leave shell.\n");
245189251Ssam			}
246189251Ssam			numeof++;
247189251Ssam		} else if (n != NULL && nflag == 0) {
248189251Ssam			job_warning = (job_warning == 2) ? 1 : 0;
249189251Ssam			numeof = 0;
250189251Ssam			evaltree(n, 0);
251189251Ssam		}
252189251Ssam		popstackmark(&smark);
253189251Ssam		setstackmark(&smark);
254189251Ssam		if (evalskip == SKIPFILE) {
255189251Ssam			evalskip = 0;
256189251Ssam			break;
257189251Ssam		}
258189251Ssam	}
259189251Ssam	popstackmark(&smark);
260189251Ssam}
261189251Ssam
262189251Ssam
263189251Ssam
264189251Ssam/*
265189251Ssam * Read /etc/profile or .profile.  Return on error.
266189251Ssam */
267189251Ssam
268189251SsamSTATIC void
269189251Ssamread_profile(char *name)
270189251Ssam{
271189251Ssam	int fd;
272189251Ssam
273189251Ssam	INTOFF;
274189251Ssam	if ((fd = open(name, O_RDONLY)) >= 0)
275189251Ssam		setinputfd(fd, 1);
276189251Ssam	INTON;
277189251Ssam	if (fd < 0)
278189251Ssam		return;
279189251Ssam	cmdloop(0);
280189251Ssam	popfile();
281189251Ssam}
282189251Ssam
283189251Ssam
284189251Ssam
285189251Ssam/*
286189251Ssam * Read a file containing shell functions.
287189251Ssam */
288189251Ssam
289189251Ssamvoid
290189251Ssamreadcmdfile(char *name)
291189251Ssam{
292189251Ssam	int fd;
293189251Ssam
294189251Ssam	INTOFF;
295189251Ssam	if ((fd = open(name, O_RDONLY)) >= 0)
296189251Ssam		setinputfd(fd, 1);
297189251Ssam	else
298189251Ssam		error("Can't open %s: %s", name, strerror(errno));
299189251Ssam	INTON;
300189251Ssam	cmdloop(0);
301189251Ssam	popfile();
302189251Ssam}
303189251Ssam
304189251Ssam
305189251Ssam
306189251Ssam/*
307189251Ssam * Take commands from a file.  To be compatible we should do a path
308189251Ssam * search for the file, which is necessary to find sub-commands.
309189251Ssam */
310189251Ssam
311189251Ssam
312189251SsamSTATIC char *
313189251Ssamfind_dot_file(char *basename)
314189251Ssam{
315189251Ssam	static char localname[FILENAME_MAX+1];
316189251Ssam	char *fullname;
317189251Ssam	char *path = pathval();
318189251Ssam	struct stat statb;
319189251Ssam
320189251Ssam	/* don't try this for absolute or relative paths */
321189251Ssam	if( strchr(basename, '/'))
322189251Ssam		return basename;
323189251Ssam
324189251Ssam	while ((fullname = padvance(&path, basename)) != NULL) {
325189251Ssam		strcpy(localname, fullname);
326189251Ssam		stunalloc(fullname);
327189251Ssam		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode))
328189251Ssam			return localname;
329189251Ssam	}
330189251Ssam	return basename;
331189251Ssam}
332189251Ssam
333189251Ssamint
334189251Ssamdotcmd(int argc, char **argv)
335189251Ssam{
336189251Ssam	struct strlist *sp;
337189251Ssam	exitstatus = 0;
338189251Ssam
339189251Ssam	for (sp = cmdenviron; sp ; sp = sp->next)
340189251Ssam		setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED);
341189251Ssam
342189251Ssam	if (argc >= 2) {		/* That's what SVR2 does */
343189251Ssam		char *fullname = find_dot_file(argv[1]);
344189251Ssam
345189251Ssam		setinputfile(fullname, 1);
346189251Ssam		commandname = fullname;
347189251Ssam		cmdloop(0);
348189251Ssam		popfile();
349189251Ssam	}
350189251Ssam	return exitstatus;
351189251Ssam}
352189251Ssam
353189251Ssam
354189251Ssamint
355189251Ssamexitcmd(int argc, char **argv)
356189251Ssam{
357189251Ssam	extern int oexitstatus;
358189251Ssam
359189251Ssam	if (stoppedjobs())
360189251Ssam		return 0;
361189251Ssam	if (argc > 1)
362189251Ssam		exitstatus = number(argv[1]);
363189251Ssam	else
364189251Ssam		exitstatus = oexitstatus;
365189251Ssam	exitshell(exitstatus);
366189251Ssam	/*NOTREACHED*/
367189251Ssam	return 0;
368189251Ssam}
369189251Ssam