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