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