sh: Do parameter expansion on ENV before using it.
[dragonfly.git] / bin / sh / main.c
... / ...
CommitLineData
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 * @(#) Copyright (c) 1991, 1993 The Regents of the University of California. All rights reserved.
37 * @(#)main.c 8.6 (Berkeley) 5/28/95
38 * $FreeBSD: src/bin/sh/main.c,v 1.50 2011/06/10 22:42:00 jilles Exp $
39 */
40
41#include <stdio.h>
42#include <signal.h>
43#include <sys/stat.h>
44#include <unistd.h>
45#include <fcntl.h>
46#include <locale.h>
47#include <errno.h>
48
49#include "shell.h"
50#include "main.h"
51#include "mail.h"
52#include "options.h"
53#include "output.h"
54#include "parser.h"
55#include "nodes.h"
56#include "expand.h"
57#include "eval.h"
58#include "jobs.h"
59#include "input.h"
60#include "trap.h"
61#include "var.h"
62#include "show.h"
63#include "memalloc.h"
64#include "error.h"
65#include "init.h"
66#include "mystring.h"
67#include "exec.h"
68#include "cd.h"
69
70int rootpid;
71int rootshell;
72struct jmploc main_handler;
73int localeisutf8, initial_localeisutf8;
74
75static void read_profile(const char *);
76static const char *find_dot_file(const char *);
77
78/*
79 * Main routine. We initialize things, parse the arguments, execute
80 * profiles if we're a login shell, and then call cmdloop to execute
81 * commands. The setjmp call sets up the location to jump to when an
82 * exception occurs. When an exception occurs the variable "state"
83 * is used to figure out how far we had gotten.
84 */
85
86int
87main(int argc, char *argv[])
88{
89 struct stackmark smark, smark2;
90 volatile int state;
91 char *shinit;
92
93 setlocale(LC_ALL, "");
94 initcharset();
95 state = 0;
96 if (setjmp(main_handler.loc)) {
97 switch (exception) {
98 case EXEXEC:
99 exitstatus = exerrno;
100 break;
101
102 case EXERROR:
103 exitstatus = 2;
104 break;
105
106 default:
107 break;
108 }
109
110 if (state == 0 || iflag == 0 || ! rootshell ||
111 exception == EXEXIT)
112 exitshell(exitstatus);
113 reset();
114 if (exception == EXINT)
115 out2fmt_flush("\n");
116 popstackmark(&smark);
117 FORCEINTON; /* enable interrupts */
118 if (state == 1)
119 goto state1;
120 else if (state == 2)
121 goto state2;
122 else if (state == 3)
123 goto state3;
124 else
125 goto state4;
126 }
127 handler = &main_handler;
128#ifdef DEBUG
129 opentrace();
130 trputs("Shell args: "); trargs(argv);
131#endif
132 rootpid = getpid();
133 rootshell = 1;
134 init();
135 setstackmark(&smark);
136 setstackmark(&smark2);
137 procargs(argc, argv);
138 pwd_init(iflag);
139 if (iflag)
140 chkmail(1);
141 if (argv[0] && argv[0][0] == '-') {
142 state = 1;
143 read_profile("/etc/profile");
144state1:
145 state = 2;
146 if (privileged == 0)
147 read_profile(".profile");
148 else
149 read_profile("/etc/suid_profile");
150 }
151state2:
152 state = 3;
153 if (!privileged && iflag) {
154 if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
155 state = 3;
156 read_profile(shinit);
157 }
158 }
159state3:
160 state = 4;
161 popstackmark(&smark2);
162 if (minusc) {
163 evalstring(minusc, sflag ? 0 : EV_EXIT);
164 }
165 if (sflag || minusc == NULL) {
166state4: /* XXX ??? - why isn't this before the "if" statement */
167 cmdloop(1);
168 }
169 exitshell(exitstatus);
170 /*NOTREACHED*/
171 return 0;
172}
173
174
175/*
176 * Read and execute commands. "Top" is nonzero for the top level command
177 * loop; it turns on prompting if the shell is interactive.
178 */
179
180void
181cmdloop(int top)
182{
183 union node *n;
184 struct stackmark smark;
185 int inter;
186 int numeof = 0;
187
188 TRACE(("cmdloop(%d) called\n", top));
189 setstackmark(&smark);
190 for (;;) {
191 if (pendingsigs)
192 dotrap();
193 inter = 0;
194 if (iflag && top) {
195 inter++;
196 showjobs(1, SHOWJOBS_DEFAULT);
197 chkmail(0);
198 flushout(&output);
199 }
200 n = parsecmd(inter);
201 /* showtree(n); DEBUG */
202 if (n == NEOF) {
203 if (!top || numeof >= 50)
204 break;
205 if (!stoppedjobs()) {
206 if (!Iflag)
207 break;
208 out2fmt_flush("\nUse \"exit\" to leave shell.\n");
209 }
210 numeof++;
211 } else if (n != NULL && nflag == 0) {
212 job_warning = (job_warning == 2) ? 1 : 0;
213 numeof = 0;
214 evaltree(n, 0);
215 }
216 popstackmark(&smark);
217 setstackmark(&smark);
218 if (evalskip != 0) {
219 if (evalskip == SKIPFILE)
220 evalskip = 0;
221 break;
222 }
223 }
224 popstackmark(&smark);
225}
226
227
228
229/*
230 * Read /etc/profile or .profile. Return on error.
231 */
232
233static void
234read_profile(const char *name)
235{
236 int fd;
237 const char *expandedname;
238
239 expandedname = expandstr(__DECONST(char *, name));
240 if (expandedname == NULL)
241 return;
242 INTOFF;
243 if ((fd = open(expandedname, O_RDONLY)) >= 0)
244 setinputfd(fd, 1);
245 INTON;
246 if (fd < 0)
247 return;
248 cmdloop(0);
249 popfile();
250}
251
252
253
254/*
255 * Read a file containing shell functions.
256 */
257
258void
259readcmdfile(const char *name)
260{
261 int fd;
262
263 INTOFF;
264 if ((fd = open(name, O_RDONLY)) >= 0)
265 setinputfd(fd, 1);
266 else
267 error("cannot open %s: %s", name, strerror(errno));
268 INTON;
269 cmdloop(0);
270 popfile();
271}
272
273
274
275/*
276 * Take commands from a file. To be compatible we should do a path
277 * search for the file, which is necessary to find sub-commands.
278 */
279
280
281static const char *
282find_dot_file(const char *basename)
283{
284 char *fullname;
285 const char *path = pathval();
286 struct stat statb;
287
288 /* don't try this for absolute or relative paths */
289 if( strchr(basename, '/'))
290 return basename;
291
292 while ((fullname = padvance(&path, basename)) != NULL) {
293 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
294 /*
295 * Don't bother freeing here, since it will
296 * be freed by the caller.
297 */
298 return fullname;
299 }
300 stunalloc(fullname);
301 }
302 return basename;
303}
304
305int
306dotcmd(int argc, char **argv)
307{
308 const char *fullname;
309 char *filename;
310
311 if (argc < 2)
312 error("missing filename");
313
314 exitstatus = 0;
315
316 /*
317 * Because we have historically not supported any options,
318 * only treat "--" specially.
319 */
320 filename = argc > 2 && strcmp(argv[1], "--") == 0 ? argv[2] : argv[1];
321
322 fullname = find_dot_file(filename);
323 setinputfile(fullname, 1);
324 commandname = fullname;
325 cmdloop(0);
326 popfile();
327 return exitstatus;
328}
329
330
331int
332exitcmd(int argc, char **argv)
333{
334 if (stoppedjobs())
335 return 0;
336 if (argc > 1)
337 exitshell(number(argv[1]));
338 else
339 exitshell_savedstatus();
340}