main.c revision 20425
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.9 1996/10/29 03:12:47 steve 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	getpwd();
175	if (argv[0] && argv[0][0] == '-') {
176		state = 1;
177		read_profile("/etc/profile");
178state1:
179		state = 2;
180		if (privileged == 0)
181			read_profile(".profile");
182		else
183			read_profile("/etc/suid_profile");
184	}
185state2:
186	state = 3;
187	if (privileged == 0) {
188		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
189			state = 3;
190			read_profile(shinit);
191		}
192	}
193state3:
194	state = 4;
195	if (minusc) {
196		evalstring(minusc);
197	}
198	if (sflag || minusc == NULL) {
199state4:	/* XXX ??? - why isn't this before the "if" statement */
200		cmdloop(1);
201	}
202#if PROFILE
203	monitor(0);
204#endif
205	exitshell(exitstatus);
206	/*NOTREACHED*/
207	return 0;
208}
209
210
211/*
212 * Read and execute commands.  "Top" is nonzero for the top level command
213 * loop; it turns on prompting if the shell is interactive.
214 */
215
216void
217cmdloop(top)
218	int top;
219{
220	union node *n;
221	struct stackmark smark;
222	int inter;
223	int numeof = 0;
224
225	TRACE(("cmdloop(%d) called\n", top));
226	setstackmark(&smark);
227	for (;;) {
228		if (pendingsigs)
229			dotrap();
230		inter = 0;
231		if (iflag && top) {
232			inter++;
233			showjobs(1);
234			chkmail(0);
235			flushout(&output);
236		}
237		n = parsecmd(inter);
238		/* showtree(n); DEBUG */
239		if (n == NEOF) {
240			if (!top || numeof >= 50)
241				break;
242			if (!stoppedjobs()) {
243				if (!Iflag)
244					break;
245				out2str("\nUse \"exit\" to leave shell.\n");
246			}
247			numeof++;
248		} else if (n != NULL && nflag == 0) {
249			job_warning = (job_warning == 2) ? 1 : 0;
250			numeof = 0;
251			evaltree(n, 0);
252		}
253		popstackmark(&smark);
254		if (evalskip == SKIPFILE) {
255			evalskip = 0;
256			break;
257		}
258	}
259	popstackmark(&smark);		/* unnecessary */
260}
261
262
263
264/*
265 * Read /etc/profile or .profile.  Return on error.
266 */
267
268STATIC void
269read_profile(name)
270	char *name;
271	{
272	int fd;
273
274	INTOFF;
275	if ((fd = open(name, O_RDONLY)) >= 0)
276		setinputfd(fd, 1);
277	INTON;
278	if (fd < 0)
279		return;
280	cmdloop(0);
281	popfile();
282}
283
284
285
286/*
287 * Read a file containing shell functions.
288 */
289
290void
291readcmdfile(name)
292	char *name;
293{
294	int fd;
295
296	INTOFF;
297	if ((fd = open(name, O_RDONLY)) >= 0)
298		setinputfd(fd, 1);
299	else
300		error("Can't open %s", name);
301	INTON;
302	cmdloop(0);
303	popfile();
304}
305
306
307
308/*
309 * Take commands from a file.  To be compatable we should do a path
310 * search for the file, which is necessary to find sub-commands.
311 */
312
313
314STATIC char *
315find_dot_file(basename)
316	char *basename;
317{
318	static char localname[FILENAME_MAX+1];
319	char *fullname;
320	char *path = pathval();
321	struct stat statb;
322
323	/* don't try this for absolute or relative paths */
324	if( strchr(basename, '/'))
325		return basename;
326
327	while ((fullname = padvance(&path, basename)) != NULL) {
328		strcpy(localname, fullname);
329		stunalloc(fullname);
330		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode))
331			return localname;
332	}
333	return basename;
334}
335
336int
337dotcmd(argc, argv)
338	int argc;
339	char **argv;
340{
341	struct strlist *sp;
342	exitstatus = 0;
343
344	for (sp = cmdenviron; sp ; sp = sp->next)
345		setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED);
346
347	if (argc >= 2) {		/* That's what SVR2 does */
348		char *fullname = find_dot_file(argv[1]);
349
350		setinputfile(fullname, 1);
351		commandname = fullname;
352		cmdloop(0);
353		popfile();
354	}
355	return exitstatus;
356}
357
358
359int
360exitcmd(argc, argv)
361	int argc;
362	char **argv;
363{
364	extern int oexitstatus;
365
366	if (stoppedjobs())
367		return 0;
368	if (argc > 1)
369		exitstatus = number(argv[1]);
370	else
371		exitstatus = oexitstatus;
372	exitshell(exitstatus);
373	/*NOTREACHED*/
374	return 0;
375}
376
377
378#ifdef notdef
379/*
380 * Should never be called.
381 */
382
383void
384exit(exitstatus) {
385	_exit(exitstatus);
386}
387#endif
388