xref: /original-bsd/bin/sh/main.c (revision b3c06cab)
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  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 static char copyright[] =
13 "@(#) Copyright (c) 1991, 1993\n\
14 	The Regents of the University of California.  All rights reserved.\n";
15 #endif /* not lint */
16 
17 #ifndef lint
18 static char sccsid[] = "@(#)main.c	8.4 (Berkeley) 05/04/95";
19 #endif /* not lint */
20 
21 #include <stdio.h>
22 #include <signal.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 
27 
28 #include "shell.h"
29 #include "main.h"
30 #include "mail.h"
31 #include "options.h"
32 #include "output.h"
33 #include "parser.h"
34 #include "nodes.h"
35 #include "eval.h"
36 #include "jobs.h"
37 #include "input.h"
38 #include "trap.h"
39 #include "var.h"
40 #include "show.h"
41 #include "memalloc.h"
42 #include "error.h"
43 #include "init.h"
44 #include "mystring.h"
45 #include "exec.h"
46 
47 #define PROFILE 0
48 
49 int rootpid;
50 int rootshell;
51 STATIC union node *curcmd;
52 STATIC union node *prevcmd;
53 extern int errno;
54 #if PROFILE
55 short profile_buf[16384];
56 extern int etext();
57 #endif
58 
59 STATIC void read_profile __P((char *));
60 STATIC char *find_dot_file __P((char *));
61 
62 /*
63  * Main routine.  We initialize things, parse the arguments, execute
64  * profiles if we're a login shell, and then call cmdloop to execute
65  * commands.  The setjmp call sets up the location to jump to when an
66  * exception occurs.  When an exception occurs the variable "state"
67  * is used to figure out how far we had gotten.
68  */
69 
70 int
71 main(argc, argv)
72 	int argc;
73 	char **argv;
74 {
75 	struct jmploc jmploc;
76 	struct stackmark smark;
77 	volatile int state;
78 	char *shinit;
79 
80 #if PROFILE
81 	monitor(4, etext, profile_buf, sizeof profile_buf, 50);
82 #endif
83 	state = 0;
84 	if (setjmp(jmploc.loc)) {
85 		/*
86 		 * When a shell procedure is executed, we raise the
87 		 * exception EXSHELLPROC to clean up before executing
88 		 * the shell procedure.
89 		 */
90 		if (exception == EXSHELLPROC) {
91 			rootpid = getpid();
92 			rootshell = 1;
93 			minusc = NULL;
94 			state = 3;
95 		} else if (state == 0 || iflag == 0 || ! rootshell)
96 			exitshell(2);
97 		reset();
98 		if (exception == EXINT
99 #if ATTY
100 		 && (! attyset() || equal(termval(), "emacs"))
101 #endif
102 		 ) {
103 			out2c('\n');
104 			flushout(&errout);
105 		}
106 		popstackmark(&smark);
107 		FORCEINTON;				/* enable interrupts */
108 		if (state == 1)
109 			goto state1;
110 		else if (state == 2)
111 			goto state2;
112 		else if (state == 3)
113 			goto state3;
114 		else
115 			goto state4;
116 	}
117 	handler = &jmploc;
118 #ifdef DEBUG
119 	opentrace();
120 	trputs("Shell args:  ");  trargs(argv);
121 #endif
122 	rootpid = getpid();
123 	rootshell = 1;
124 	init();
125 	setstackmark(&smark);
126 	procargs(argc, argv);
127 	if (argv[0] && argv[0][0] == '-') {
128 		state = 1;
129 		read_profile("/etc/profile");
130 state1:
131 		state = 2;
132 		read_profile(".profile");
133 	}
134 state2:
135 	state = 3;
136 	if (getuid() == geteuid() && getgid() == getegid()) {
137 		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
138 			state = 3;
139 			read_profile(shinit);
140 		}
141 	}
142 state3:
143 	state = 4;
144 	if (minusc) {
145 		evalstring(minusc);
146 	}
147 	if (sflag || minusc == NULL) {
148 state4:	/* XXX ??? - why isn't this before the "if" statement */
149 		cmdloop(1);
150 	}
151 #if PROFILE
152 	monitor(0);
153 #endif
154 	exitshell(exitstatus);
155 	/*NOTREACHED*/
156 	return 0;
157 }
158 
159 
160 /*
161  * Read and execute commands.  "Top" is nonzero for the top level command
162  * loop; it turns on prompting if the shell is interactive.
163  */
164 
165 void
166 cmdloop(top)
167 	int top;
168 {
169 	union node *n;
170 	struct stackmark smark;
171 	int inter;
172 	int numeof = 0;
173 
174 	TRACE(("cmdloop(%d) called\n", top));
175 	setstackmark(&smark);
176 	for (;;) {
177 		if (pendingsigs)
178 			dotrap();
179 		inter = 0;
180 		if (iflag && top) {
181 			inter++;
182 			showjobs(1);
183 			chkmail(0);
184 			flushout(&output);
185 		}
186 		n = parsecmd(inter);
187 		/* showtree(n); DEBUG */
188 		if (n == NEOF) {
189 			if (!top || numeof >= 50)
190 				break;
191 			if (!stoppedjobs()) {
192 				if (!Iflag)
193 					break;
194 				out2str("\nUse \"exit\" to leave shell.\n");
195 			}
196 			numeof++;
197 		} else if (n != NULL && nflag == 0) {
198 			job_warning = (job_warning == 2) ? 1 : 0;
199 			numeof = 0;
200 			evaltree(n, 0);
201 		}
202 		popstackmark(&smark);
203 	}
204 	popstackmark(&smark);		/* unnecessary */
205 }
206 
207 
208 
209 /*
210  * Read /etc/profile or .profile.  Return on error.
211  */
212 
213 STATIC void
214 read_profile(name)
215 	char *name;
216 	{
217 	int fd;
218 
219 	INTOFF;
220 	if ((fd = open(name, O_RDONLY)) >= 0)
221 		setinputfd(fd, 1);
222 	INTON;
223 	if (fd < 0)
224 		return;
225 	cmdloop(0);
226 	popfile();
227 }
228 
229 
230 
231 /*
232  * Read a file containing shell functions.
233  */
234 
235 void
236 readcmdfile(name)
237 	char *name;
238 {
239 	int fd;
240 
241 	INTOFF;
242 	if ((fd = open(name, O_RDONLY)) >= 0)
243 		setinputfd(fd, 1);
244 	else
245 		error("Can't open %s", name);
246 	INTON;
247 	cmdloop(0);
248 	popfile();
249 }
250 
251 
252 
253 /*
254  * Take commands from a file.  To be compatable we should do a path
255  * search for the file, which is necessary to find sub-commands.
256  */
257 
258 
259 STATIC char *
260 find_dot_file(basename)
261 	char *basename;
262 {
263 	static char localname[FILENAME_MAX+1];
264 	char *fullname;
265 	char *path = pathval();
266 	struct stat statb;
267 
268 	/* don't try this for absolute or relative paths */
269 	if( strchr(basename, '/'))
270 		return basename;
271 
272 	while ((fullname = padvance(&path, basename)) != NULL) {
273 		strcpy(localname, fullname);
274 		stunalloc(fullname);
275 		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode))
276 			return localname;
277 	}
278 	return basename;
279 }
280 
281 int
282 dotcmd(argc, argv)
283 	int argc;
284 	char **argv;
285 {
286 	exitstatus = 0;
287 	if (argc >= 2) {		/* That's what SVR2 does */
288 		char *fullname = find_dot_file(argv[1]);
289 		setinputfile(fullname, 1);
290 		commandname = fullname;
291 		cmdloop(0);
292 		popfile();
293 	}
294 	return exitstatus;
295 }
296 
297 
298 int
299 exitcmd(argc, argv)
300 	int argc;
301 	char **argv;
302 {
303 	if (stoppedjobs())
304 		return 0;
305 	if (argc > 1)
306 		exitstatus = number(argv[1]);
307 	exitshell(exitstatus);
308 	/*NOTREACHED*/
309 	return 0;
310 }
311 
312 
313 #ifdef notdef
314 /*
315  * Should never be called.
316  */
317 
318 void
319 exit(exitstatus) {
320 	_exit(exitstatus);
321 }
322 #endif
323