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