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