1 /*- 2 * Copyright (c) 1991 The Regents of the University of California. 3 * 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 char copyright[] = 13 "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ 14 All rights reserved.\n"; 15 #endif /* not lint */ 16 17 #ifndef lint 18 static char sccsid[] = "@(#)main.c 5.7 (Berkeley) 07/16/92"; 19 #endif /* not lint */ 20 21 #include <signal.h> 22 #include <fcntl.h> 23 #include "shell.h" 24 #include "main.h" 25 #include "mail.h" 26 #include "options.h" 27 #include "output.h" 28 #include "parser.h" 29 #include "nodes.h" 30 #include "eval.h" 31 #include "jobs.h" 32 #include "input.h" 33 #include "trap.h" 34 #include "var.h" 35 #include "memalloc.h" 36 #include "error.h" 37 #include "init.h" 38 #include "mystring.h" 39 40 #define PROFILE 0 41 42 int rootpid; 43 int rootshell; 44 STATIC union node *curcmd; 45 STATIC union node *prevcmd; 46 extern int errno; 47 #if PROFILE 48 short profile_buf[16384]; 49 extern int etext(); 50 #endif 51 52 #ifdef __STDC__ 53 STATIC void read_profile(char *); 54 char *getenv(char *); 55 #else 56 STATIC void read_profile(); 57 char *getenv(); 58 #endif 59 60 61 /* 62 * Main routine. We initialize things, parse the arguments, execute 63 * profiles if we're a login shell, and then call cmdloop to execute 64 * commands. The setjmp call sets up the location to jump to when an 65 * exception occurs. When an exception occurs the variable "state" 66 * is used to figure out how far we had gotten. 67 */ 68 69 main(argc, argv) char **argv; { 70 struct jmploc jmploc; 71 struct stackmark smark; 72 volatile int state; 73 char *shinit; 74 75 #if PROFILE 76 monitor(4, etext, profile_buf, sizeof profile_buf, 50); 77 #endif 78 state = 0; 79 if (setjmp(jmploc.loc)) { 80 /* 81 * When a shell procedure is executed, we raise the 82 * exception EXSHELLPROC to clean up before executing 83 * the shell procedure. 84 */ 85 if (exception == EXSHELLPROC) { 86 rootpid = getpid(); 87 rootshell = 1; 88 minusc = NULL; 89 state = 3; 90 } else if (state == 0 || iflag == 0 || ! rootshell) 91 exitshell(2); 92 reset(); 93 #if ATTY 94 if (exception == EXINT 95 && (! attyset() || equal(termval(), "emacs"))) { 96 #else 97 if (exception == EXINT) { 98 #endif 99 out2c('\n'); 100 flushout(&errout); 101 } 102 popstackmark(&smark); 103 FORCEINTON; /* enable interrupts */ 104 if (state == 1) 105 goto state1; 106 else if (state == 2) 107 goto state2; 108 else if (state == 3) 109 goto state3; 110 else 111 goto state4; 112 } 113 handler = &jmploc; 114 #ifdef DEBUG 115 opentrace(); 116 trputs("Shell args: "); trargs(argv); 117 #endif 118 rootpid = getpid(); 119 rootshell = 1; 120 init(); 121 setstackmark(&smark); 122 procargs(argc, argv); 123 if (argv[0] && argv[0][0] == '-') { 124 state = 1; 125 read_profile("/etc/profile"); 126 state1: 127 state = 2; 128 read_profile(".profile"); 129 } 130 state2: 131 state = 3; 132 if ((shinit = lookupvar("ENV")) != NULL && 133 *shinit != '\0') { 134 state = 3; 135 read_profile(shinit); 136 } 137 state3: 138 state = 4; 139 if (minusc) { 140 evalstring(minusc); 141 } 142 if (sflag || minusc == NULL) { 143 state4: /* XXX ??? - why isn't this before the "if" statement */ 144 cmdloop(1); 145 } 146 #if PROFILE 147 monitor(0); 148 #endif 149 exitshell(exitstatus); 150 } 151 152 153 /* 154 * Read and execute commands. "Top" is nonzero for the top level command 155 * loop; it turns on prompting if the shell is interactive. 156 */ 157 158 void 159 cmdloop(top) { 160 union node *n; 161 struct stackmark smark; 162 int inter; 163 int numeof = 0; 164 165 TRACE(("cmdloop(%d) called\n", top)); 166 setstackmark(&smark); 167 for (;;) { 168 if (pendingsigs) 169 dotrap(); 170 inter = 0; 171 if (iflag && top) { 172 inter++; 173 showjobs(1); 174 chkmail(0); 175 flushout(&output); 176 } 177 n = parsecmd(inter); 178 /* showtree(n); DEBUG */ 179 if (n == NEOF) { 180 if (!top || numeof >= 50) 181 break; 182 if (!stoppedjobs()) { 183 if (!Iflag) 184 break; 185 out2str("\nUse \"exit\" to leave shell.\n"); 186 } 187 numeof++; 188 } else if (n != NULL && nflag == 0) { 189 job_warning = (job_warning == 2) ? 1 : 0; 190 numeof = 0; 191 evaltree(n, 0); 192 } 193 popstackmark(&smark); 194 } 195 popstackmark(&smark); /* unnecessary */ 196 } 197 198 199 200 /* 201 * Read /etc/profile or .profile. Return on error. 202 */ 203 204 STATIC void 205 read_profile(name) 206 char *name; 207 { 208 int fd; 209 210 INTOFF; 211 if ((fd = open(name, O_RDONLY)) >= 0) 212 setinputfd(fd, 1); 213 INTON; 214 if (fd < 0) 215 return; 216 cmdloop(0); 217 popfile(); 218 } 219 220 221 222 /* 223 * Read a file containing shell functions. 224 */ 225 226 void 227 readcmdfile(name) 228 char *name; 229 { 230 int fd; 231 232 INTOFF; 233 if ((fd = open(name, O_RDONLY)) >= 0) 234 setinputfd(fd, 1); 235 else 236 error("Can't open %s", name); 237 INTON; 238 cmdloop(0); 239 popfile(); 240 } 241 242 243 244 /* 245 * Take commands from a file. To be compatable we should do a path 246 * search for the file, but a path search doesn't make any sense. 247 */ 248 249 dotcmd(argc, argv) char **argv; { 250 exitstatus = 0; 251 if (argc >= 2) { /* That's what SVR2 does */ 252 setinputfile(argv[1], 1); 253 commandname = argv[1]; 254 cmdloop(0); 255 popfile(); 256 } 257 return exitstatus; 258 } 259 260 261 exitcmd(argc, argv) char **argv; { 262 if (stoppedjobs()) 263 return; 264 if (argc > 1) 265 exitstatus = number(argv[1]); 266 exitshell(exitstatus); 267 } 268 269 270 #ifdef notdef 271 /* 272 * Should never be called. 273 */ 274 275 void 276 exit(exitstatus) { 277 _exit(exitstatus); 278 } 279 #endif 280