main.c revision 22988
1235911Smav/*-
2235911Smav * Copyright (c) 1991, 1993
3235911Smav *	The Regents of the University of California.  All rights reserved.
4235911Smav *
5235911Smav * This code is derived from software contributed to Berkeley by
6235911Smav * Kenneth Almquist.
7235911Smav *
8235911Smav * Redistribution and use in source and binary forms, with or without
9235911Smav * modification, are permitted provided that the following conditions
10235911Smav * are met:
11235911Smav * 1. Redistributions of source code must retain the above copyright
12235911Smav *    notice, this list of conditions and the following disclaimer.
13235911Smav * 2. Redistributions in binary form must reproduce the above copyright
14235911Smav *    notice, this list of conditions and the following disclaimer in the
15235911Smav *    documentation and/or other materials provided with the distribution.
16235911Smav * 3. All advertising materials mentioning features or use of this software
17235911Smav *    must display the following acknowledgement:
18235911Smav *	This product includes software developed by the University of
19235911Smav *	California, Berkeley and its contributors.
20235911Smav * 4. Neither the name of the University nor the names of its contributors
21235911Smav *    may be used to endorse or promote products derived from this software
22235911Smav *    without specific prior written permission.
23235911Smav *
24235911Smav * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25235911Smav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26235911Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27235911Smav * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28235911Smav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29235911Smav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30235911Smav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31235911Smav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32235911Smav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33235911Smav * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34235911Smav * SUCH DAMAGE.
35235911Smav *
36235911Smav *	$Id$
37235911Smav */
38235911Smav
39235911Smav#ifndef lint
40235911Smavstatic char const copyright[] =
41235911Smav"@(#) Copyright (c) 1991, 1993\n\
42235911Smav	The Regents of the University of California.  All rights reserved.\n";
43235911Smav#endif /* not lint */
44235911Smav
45235911Smav#ifndef lint
46235911Smavstatic char const sccsid[] = "@(#)main.c	8.6 (Berkeley) 5/28/95";
47235911Smav#endif /* not lint */
48235911Smav
49235911Smav#include <stdio.h>
50235911Smav#include <signal.h>
51235911Smav#include <sys/stat.h>
52235911Smav#include <unistd.h>
53235911Smav#include <fcntl.h>
54235911Smav#include <locale.h>
55235911Smav
56235911Smav
57235911Smav#include "shell.h"
58235911Smav#include "main.h"
59235911Smav#include "mail.h"
60235911Smav#include "options.h"
61235911Smav#include "output.h"
62235911Smav#include "parser.h"
63235911Smav#include "nodes.h"
64235911Smav#include "expand.h"
65235911Smav#include "eval.h"
66235911Smav#include "jobs.h"
67235911Smav#include "input.h"
68235911Smav#include "trap.h"
69235911Smav#include "var.h"
70235911Smav#include "show.h"
71235911Smav#include "memalloc.h"
72235911Smav#include "error.h"
73235911Smav#include "init.h"
74235911Smav#include "mystring.h"
75235911Smav#include "exec.h"
76235911Smav#include "cd.h"
77235911Smav
78235911Smav#define PROFILE 0
79235911Smav
80235911Smavint rootpid;
81235911Smavint rootshell;
82235911Smavextern int errno;
83235911Smav#if PROFILE
84235911Smavshort profile_buf[16384];
85235911Smavextern int etext();
86235911Smav#endif
87235911Smav
88235911SmavSTATIC void read_profile __P((char *));
89235911SmavSTATIC char *find_dot_file __P((char *));
90235911Smav
91235911Smav/*
92235911Smav * Main routine.  We initialize things, parse the arguments, execute
93235911Smav * profiles if we're a login shell, and then call cmdloop to execute
94235911Smav * commands.  The setjmp call sets up the location to jump to when an
95235911Smav * exception occurs.  When an exception occurs the variable "state"
96235911Smav * is used to figure out how far we had gotten.
97235911Smav */
98235911Smav
99235911Smavint
100235911Smavmain(argc, argv)
101235911Smav	int argc;
102235911Smav	char **argv;
103235911Smav{
104235911Smav	struct jmploc jmploc;
105235911Smav	struct stackmark smark;
106235911Smav	volatile int state;
107235911Smav	char *shinit;
108235911Smav
109235911Smav#if PROFILE
110235911Smav	monitor(4, etext, profile_buf, sizeof profile_buf, 50);
111235911Smav#endif
112235911Smav	(void) setlocale(LC_ALL, "");
113235911Smav	state = 0;
114235911Smav	if (setjmp(jmploc.loc)) {
115235911Smav		/*
116235911Smav		 * When a shell procedure is executed, we raise the
117235911Smav		 * exception EXSHELLPROC to clean up before executing
118235911Smav		 * the shell procedure.
119235911Smav		 */
120235911Smav		switch (exception) {
121235911Smav		case EXSHELLPROC:
122235911Smav			rootpid = getpid();
123235911Smav			rootshell = 1;
124235911Smav			minusc = NULL;
125235911Smav			state = 3;
126235911Smav			break;
127235911Smav
128235911Smav		case EXEXEC:
129235911Smav			exitstatus = exerrno;
130235911Smav			break;
131235911Smav
132235911Smav		case EXERROR:
133235911Smav			exitstatus = 2;
134235911Smav			break;
135235911Smav
136235911Smav		default:
137235911Smav			break;
138235911Smav		}
139235911Smav
140235911Smav		if (exception != EXSHELLPROC) {
141235911Smav		    if (state == 0 || iflag == 0 || ! rootshell)
142235911Smav			    exitshell(exitstatus);
143235911Smav		}
144235911Smav		reset();
145235911Smav		if (exception == EXINT
146235911Smav#if ATTY
147235911Smav		 && (! attyset() || equal(termval(), "emacs"))
148235911Smav#endif
149235911Smav		 ) {
150235911Smav			out2c('\n');
151235911Smav			flushout(&errout);
152235911Smav		}
153235911Smav		popstackmark(&smark);
154235911Smav		FORCEINTON;				/* enable interrupts */
155235911Smav		if (state == 1)
156235911Smav			goto state1;
157235911Smav		else if (state == 2)
158235911Smav			goto state2;
159235911Smav		else if (state == 3)
160235911Smav			goto state3;
161235911Smav		else
162235911Smav			goto state4;
163235911Smav	}
164235911Smav	handler = &jmploc;
165235911Smav#ifdef DEBUG
166235911Smav	opentrace();
167235911Smav	trputs("Shell args:  ");  trargs(argv);
168235911Smav#endif
169235911Smav	rootpid = getpid();
170235911Smav	rootshell = 1;
171235911Smav	init();
172235911Smav	setstackmark(&smark);
173235911Smav	procargs(argc, argv);
174235911Smav	if (getpwd() == NULL && iflag)
175235911Smav		out2str("sh: cannot determine working directory\n");
176235911Smav	if (argv[0] && argv[0][0] == '-') {
177235911Smav		state = 1;
178235911Smav		read_profile("/etc/profile");
179235911Smavstate1:
180235911Smav		state = 2;
181235911Smav		if (privileged == 0)
182235911Smav			read_profile(".profile");
183235911Smav		else
184235911Smav			read_profile("/etc/suid_profile");
185235911Smav	}
186235911Smavstate2:
187235911Smav	state = 3;
188235911Smav	if (privileged == 0) {
189235911Smav		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
190235911Smav			state = 3;
191235911Smav			read_profile(shinit);
192235911Smav		}
193235911Smav	}
194235911Smavstate3:
195235911Smav	state = 4;
196235911Smav	if (minusc) {
197235911Smav		evalstring(minusc);
198235911Smav	}
199235911Smav	if (sflag || minusc == NULL) {
200235911Smavstate4:	/* XXX ??? - why isn't this before the "if" statement */
201235911Smav		cmdloop(1);
202235911Smav	}
203235911Smav#if PROFILE
204235911Smav	monitor(0);
205235911Smav#endif
206235911Smav	exitshell(exitstatus);
207235911Smav	/*NOTREACHED*/
208235911Smav	return 0;
209235911Smav}
210235911Smav
211235911Smav
212235911Smav/*
213235911Smav * Read and execute commands.  "Top" is nonzero for the top level command
214235911Smav * loop; it turns on prompting if the shell is interactive.
215235911Smav */
216235911Smav
217235911Smavvoid
218235911Smavcmdloop(top)
219235911Smav	int top;
220235911Smav{
221235911Smav	union node *n;
222235911Smav	struct stackmark smark;
223239213Smjacob	int inter;
224235911Smav	int numeof = 0;
225235911Smav
226235911Smav	TRACE(("cmdloop(%d) called\n", top));
227235911Smav	setstackmark(&smark);
228235911Smav	for (;;) {
229235911Smav		if (pendingsigs)
230235911Smav			dotrap();
231235911Smav		inter = 0;
232235911Smav		if (iflag && top) {
233235911Smav			inter++;
234235911Smav			showjobs(1);
235235911Smav			chkmail(0);
236235911Smav			flushout(&output);
237235911Smav		}
238235911Smav		n = parsecmd(inter);
239235911Smav		/* showtree(n); DEBUG */
240235911Smav		if (n == NEOF) {
241235911Smav			if (!top || numeof >= 50)
242235911Smav				break;
243235911Smav			if (!stoppedjobs()) {
244235911Smav				if (!Iflag)
245235911Smav					break;
246260387Sscottl				out2str("\nUse \"exit\" to leave shell.\n");
247235911Smav			}
248235911Smav			numeof++;
249235911Smav		} else if (n != NULL && nflag == 0) {
250235911Smav			job_warning = (job_warning == 2) ? 1 : 0;
251260387Sscottl			numeof = 0;
252235911Smav			evaltree(n, 0);
253235911Smav		}
254235911Smav		popstackmark(&smark);
255235911Smav		if (evalskip == SKIPFILE) {
256235911Smav			evalskip = 0;
257235911Smav			break;
258235911Smav		}
259235911Smav	}
260235911Smav	popstackmark(&smark);		/* unnecessary */
261235911Smav}
262235911Smav
263235911Smav
264235911Smav
265235911Smav/*
266235911Smav * Read /etc/profile or .profile.  Return on error.
267235911Smav */
268235911Smav
269235911SmavSTATIC void
270235911Smavread_profile(name)
271235911Smav	char *name;
272239213Smjacob	{
273235911Smav	int fd;
274235911Smav
275235911Smav	INTOFF;
276235911Smav	if ((fd = open(name, O_RDONLY)) >= 0)
277235911Smav		setinputfd(fd, 1);
278235911Smav	INTON;
279235911Smav	if (fd < 0)
280235911Smav		return;
281235911Smav	cmdloop(0);
282235911Smav	popfile();
283235911Smav}
284235911Smav
285235911Smav
286235911Smav
287235911Smav/*
288235911Smav * Read a file containing shell functions.
289235911Smav */
290235911Smav
291235911Smavvoid
292235911Smavreadcmdfile(name)
293235911Smav	char *name;
294235911Smav{
295235911Smav	int fd;
296235911Smav
297235911Smav	INTOFF;
298235911Smav	if ((fd = open(name, O_RDONLY)) >= 0)
299235911Smav		setinputfd(fd, 1);
300235911Smav	else
301235911Smav		error("Can't open %s", name);
302235911Smav	INTON;
303235911Smav	cmdloop(0);
304235911Smav	popfile();
305235911Smav}
306235911Smav
307235911Smav
308235911Smav
309235911Smav/*
310235911Smav * Take commands from a file.  To be compatable we should do a path
311235911Smav * search for the file, which is necessary to find sub-commands.
312235911Smav */
313235911Smav
314235911Smav
315235911SmavSTATIC char *
316235911Smavfind_dot_file(basename)
317235911Smav	char *basename;
318235911Smav{
319235911Smav	static char localname[FILENAME_MAX+1];
320235911Smav	char *fullname;
321235911Smav	char *path = pathval();
322235911Smav	struct stat statb;
323235911Smav
324235911Smav	/* don't try this for absolute or relative paths */
325235911Smav	if( strchr(basename, '/'))
326235911Smav		return basename;
327235911Smav
328235911Smav	while ((fullname = padvance(&path, basename)) != NULL) {
329235911Smav		strcpy(localname, fullname);
330235911Smav		stunalloc(fullname);
331235911Smav		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode))
332235911Smav			return localname;
333235911Smav	}
334235911Smav	return basename;
335235911Smav}
336235911Smav
337235911Smavint
338235911Smavdotcmd(argc, argv)
339235911Smav	int argc;
340235911Smav	char **argv;
341235911Smav{
342235911Smav	struct strlist *sp;
343235911Smav	exitstatus = 0;
344235911Smav
345235911Smav	for (sp = cmdenviron; sp ; sp = sp->next)
346235911Smav		setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED);
347235911Smav
348235911Smav	if (argc >= 2) {		/* That's what SVR2 does */
349235911Smav		char *fullname = find_dot_file(argv[1]);
350235911Smav
351235911Smav		setinputfile(fullname, 1);
352235911Smav		commandname = fullname;
353235911Smav		cmdloop(0);
354235911Smav		popfile();
355235911Smav	}
356235911Smav	return exitstatus;
357235911Smav}
358235911Smav
359235911Smav
360235911Smavint
361235911Smavexitcmd(argc, argv)
362235911Smav	int argc;
363235911Smav	char **argv;
364235911Smav{
365235911Smav	extern int oexitstatus;
366235911Smav
367235911Smav	if (stoppedjobs())
368235911Smav		return 0;
369235911Smav	if (argc > 1)
370235911Smav		exitstatus = number(argv[1]);
371235911Smav	else
372235911Smav		exitstatus = oexitstatus;
373235911Smav	exitshell(exitstatus);
374235911Smav	/*NOTREACHED*/
375235911Smav	return 0;
376235911Smav}
377235911Smav
378235911Smav
379235911Smav#ifdef notdef
380235911Smav/*
381235911Smav * Should never be called.
382235911Smav */
383235911Smav
384235911Smavvoid
385235911Smavexit(exitstatus) {
386235911Smav	_exit(exitstatus);
387235911Smav}
388235911Smav#endif
389235911Smav