main.c revision 25471
1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	$Id: main.c,v 1.13 1997/02/22 13:58:33 peter Exp $
37 */
38
39#ifndef lint
40static char const copyright[] =
41"@(#) Copyright (c) 1991, 1993\n\
42	The Regents of the University of California.  All rights reserved.\n";
43#endif /* not lint */
44
45#ifndef lint
46static char const sccsid[] = "@(#)main.c	8.6 (Berkeley) 5/28/95";
47#endif /* not lint */
48
49#include <stdio.h>
50#include <signal.h>
51#include <sys/stat.h>
52#include <unistd.h>
53#include <fcntl.h>
54#include <locale.h>
55
56
57#include "shell.h"
58#include "main.h"
59#include "mail.h"
60#include "options.h"
61#include "output.h"
62#include "parser.h"
63#include "nodes.h"
64#include "expand.h"
65#include "eval.h"
66#include "jobs.h"
67#include "input.h"
68#include "trap.h"
69#include "var.h"
70#include "show.h"
71#include "memalloc.h"
72#include "error.h"
73#include "init.h"
74#include "mystring.h"
75#include "exec.h"
76#include "cd.h"
77
78#define PROFILE 0
79
80int rootpid;
81int rootshell;
82extern int errno;
83#if PROFILE
84short profile_buf[16384];
85extern int etext();
86#endif
87
88STATIC void read_profile __P((char *));
89STATIC char *find_dot_file __P((char *));
90
91/*
92 * Main routine.  We initialize things, parse the arguments, execute
93 * profiles if we're a login shell, and then call cmdloop to execute
94 * commands.  The setjmp call sets up the location to jump to when an
95 * exception occurs.  When an exception occurs the variable "state"
96 * is used to figure out how far we had gotten.
97 */
98
99int
100main(argc, argv)
101	int argc;
102	char **argv;
103{
104	struct jmploc jmploc;
105	struct stackmark smark;
106	volatile int state;
107	char *shinit;
108
109#if PROFILE
110	monitor(4, etext, profile_buf, sizeof profile_buf, 50);
111#endif
112	(void) setlocale(LC_ALL, "");
113	state = 0;
114	if (setjmp(jmploc.loc)) {
115		/*
116		 * When a shell procedure is executed, we raise the
117		 * exception EXSHELLPROC to clean up before executing
118		 * the shell procedure.
119		 */
120		switch (exception) {
121		case EXSHELLPROC:
122			rootpid = getpid();
123			rootshell = 1;
124			minusc = NULL;
125			state = 3;
126			break;
127
128		case EXEXEC:
129			exitstatus = exerrno;
130			break;
131
132		case EXERROR:
133			exitstatus = 2;
134			break;
135
136		default:
137			break;
138		}
139
140		if (exception != EXSHELLPROC) {
141		    if (state == 0 || iflag == 0 || ! rootshell)
142			    exitshell(exitstatus);
143		}
144		reset();
145		if (exception == EXINT
146#if ATTY
147		 && (! attyset() || equal(termval(), "emacs"))
148#endif
149		 ) {
150			out2c('\n');
151			flushout(&errout);
152		}
153		popstackmark(&smark);
154		FORCEINTON;				/* enable interrupts */
155		if (state == 1)
156			goto state1;
157		else if (state == 2)
158			goto state2;
159		else if (state == 3)
160			goto state3;
161		else
162			goto state4;
163	}
164	handler = &jmploc;
165#ifdef DEBUG
166	opentrace();
167	trputs("Shell args:  ");  trargs(argv);
168#endif
169	rootpid = getpid();
170	rootshell = 1;
171	init();
172	setstackmark(&smark);
173	procargs(argc, argv);
174	if (getpwd() == NULL && iflag)
175		out2str("sh: cannot determine working directory\n");
176	if (argv[0] && argv[0][0] == '-') {
177		state = 1;
178		read_profile("/etc/profile");
179state1:
180		state = 2;
181		if (privileged == 0)
182			read_profile(".profile");
183		else
184			read_profile("/etc/suid_profile");
185	}
186state2:
187	state = 3;
188	if (!privileged && iflag) {
189		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
190			state = 3;
191			read_profile(shinit);
192		}
193	}
194state3:
195	state = 4;
196	if (minusc) {
197		evalstring(minusc);
198	}
199	if (sflag || minusc == NULL) {
200state4:	/* XXX ??? - why isn't this before the "if" statement */
201		cmdloop(1);
202	}
203#if PROFILE
204	monitor(0);
205#endif
206	exitshell(exitstatus);
207	/*NOTREACHED*/
208	return 0;
209}
210
211
212/*
213 * Read and execute commands.  "Top" is nonzero for the top level command
214 * loop; it turns on prompting if the shell is interactive.
215 */
216
217void
218cmdloop(top)
219	int top;
220{
221	union node *n;
222	struct stackmark smark;
223	int inter;
224	int numeof = 0;
225
226	TRACE(("cmdloop(%d) called\n", top));
227	setstackmark(&smark);
228	for (;;) {
229		if (pendingsigs)
230			dotrap();
231		inter = 0;
232		if (iflag && top) {
233			inter++;
234			showjobs(1);
235			chkmail(0);
236			flushout(&output);
237		}
238		n = parsecmd(inter);
239		/* showtree(n); DEBUG */
240		if (n == NEOF) {
241			if (!top || numeof >= 50)
242				break;
243			if (!stoppedjobs()) {
244				if (!Iflag)
245					break;
246				out2str("\nUse \"exit\" to leave shell.\n");
247			}
248			numeof++;
249		} else if (n != NULL && nflag == 0) {
250			job_warning = (job_warning == 2) ? 1 : 0;
251			numeof = 0;
252			evaltree(n, 0);
253		}
254		popstackmark(&smark);
255		if (evalskip == SKIPFILE) {
256			evalskip = 0;
257			break;
258		}
259	}
260	popstackmark(&smark);		/* unnecessary */
261}
262
263
264
265/*
266 * Read /etc/profile or .profile.  Return on error.
267 */
268
269STATIC void
270read_profile(name)
271	char *name;
272	{
273	int fd;
274
275	INTOFF;
276	if ((fd = open(name, O_RDONLY)) >= 0)
277		setinputfd(fd, 1);
278	INTON;
279	if (fd < 0)
280		return;
281	cmdloop(0);
282	popfile();
283}
284
285
286
287/*
288 * Read a file containing shell functions.
289 */
290
291void
292readcmdfile(name)
293	char *name;
294{
295	int fd;
296
297	INTOFF;
298	if ((fd = open(name, O_RDONLY)) >= 0)
299		setinputfd(fd, 1);
300	else
301		error("Can't open %s", name);
302	INTON;
303	cmdloop(0);
304	popfile();
305}
306
307
308
309/*
310 * Take commands from a file.  To be compatable we should do a path
311 * search for the file, which is necessary to find sub-commands.
312 */
313
314
315STATIC char *
316find_dot_file(basename)
317	char *basename;
318{
319	static char localname[FILENAME_MAX+1];
320	char *fullname;
321	char *path = pathval();
322	struct stat statb;
323
324	/* don't try this for absolute or relative paths */
325	if( strchr(basename, '/'))
326		return basename;
327
328	while ((fullname = padvance(&path, basename)) != NULL) {
329		strcpy(localname, fullname);
330		stunalloc(fullname);
331		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode))
332			return localname;
333	}
334	return basename;
335}
336
337int
338dotcmd(argc, argv)
339	int argc;
340	char **argv;
341{
342	struct strlist *sp;
343	exitstatus = 0;
344
345	for (sp = cmdenviron; sp ; sp = sp->next)
346		setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED);
347
348	if (argc >= 2) {		/* That's what SVR2 does */
349		char *fullname = find_dot_file(argv[1]);
350
351		setinputfile(fullname, 1);
352		commandname = fullname;
353		cmdloop(0);
354		popfile();
355	}
356	return exitstatus;
357}
358
359
360int
361exitcmd(argc, argv)
362	int argc;
363	char **argv;
364{
365	extern int oexitstatus;
366
367	if (stoppedjobs())
368		return 0;
369	if (argc > 1)
370		exitstatus = number(argv[1]);
371	else
372		exitstatus = oexitstatus;
373	exitshell(exitstatus);
374	/*NOTREACHED*/
375	return 0;
376}
377
378
379#ifdef notdef
380/*
381 * Should never be called.
382 */
383
384void
385exit(exitstatus) {
386	_exit(exitstatus);
387}
388#endif
389