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.1 (Berkeley) 03/07/91"; 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 #if ATTY 35 #include "var.h" 36 #endif 37 #include "memalloc.h" 38 #include "error.h" 39 #include "init.h" 40 #include "mystring.h" 41 42 #define PROFILE 0 43 44 int rootpid; 45 int rootshell; 46 STATIC union node *curcmd; 47 STATIC union node *prevcmd; 48 extern int errno; 49 #if PROFILE 50 short profile_buf[16384]; 51 extern int etext(); 52 #endif 53 54 #ifdef __STDC__ 55 STATIC void read_profile(char *); 56 char *getenv(char *); 57 #else 58 STATIC void read_profile(); 59 char *getenv(); 60 #endif 61 62 63 /* 64 * Main routine. We initialize things, parse the arguments, execute 65 * profiles if we're a login shell, and then call cmdloop to execute 66 * commands. The setjmp call sets up the location to jump to when an 67 * exception occurs. When an exception occurs the variable "state" 68 * is used to figure out how far we had gotten. 69 */ 70 71 main(argc, argv) char **argv; { 72 struct jmploc jmploc; 73 struct stackmark smark; 74 volatile int state; 75 char *shinit; 76 77 #if PROFILE 78 monitor(4, etext, profile_buf, sizeof profile_buf, 50); 79 #endif 80 state = 0; 81 if (setjmp(jmploc.loc)) { 82 /* 83 * When a shell procedure is executed, we raise the 84 * exception EXSHELLPROC to clean up before executing 85 * the shell procedure. 86 */ 87 if (exception == EXSHELLPROC) { 88 rootpid = getpid(); 89 rootshell = 1; 90 minusc = NULL; 91 state = 3; 92 } else if (state == 0 || iflag == 0 || ! rootshell) 93 exitshell(2); 94 reset(); 95 #if ATTY 96 if (exception == EXINT 97 && (! attyset() || equal(termval(), "emacs"))) { 98 #else 99 if (exception == EXINT) { 100 #endif 101 out2c('\n'); 102 flushout(&errout); 103 } 104 popstackmark(&smark); 105 FORCEINTON; /* enable interrupts */ 106 if (state == 1) 107 goto state1; 108 else if (state == 2) 109 goto state2; 110 else 111 goto state3; 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 } else if ((sflag || minusc) && (shinit = getenv("SHINIT")) != NULL) { 130 state = 2; 131 evalstring(shinit); 132 } 133 state2: 134 state = 3; 135 if (minusc) { 136 evalstring(minusc); 137 } 138 if (sflag || minusc == NULL) { 139 state3: 140 cmdloop(1); 141 } 142 #if PROFILE 143 monitor(0); 144 #endif 145 exitshell(exitstatus); 146 } 147 148 149 /* 150 * Read and execute commands. "Top" is nonzero for the top level command 151 * loop; it turns on prompting if the shell is interactive. 152 */ 153 154 void 155 cmdloop(top) { 156 union node *n; 157 struct stackmark smark; 158 int inter; 159 int numeof; 160 161 TRACE(("cmdloop(%d) called\n", top)); 162 setstackmark(&smark); 163 numeof = 0; 164 for (;;) { 165 if (pendingsigs) 166 dotrap(); 167 inter = 0; 168 if (iflag && top) { 169 inter++; 170 showjobs(1); 171 chkmail(0); 172 flushout(&output); 173 } 174 n = parsecmd(inter); 175 #ifdef DEBUG 176 /* BROKEN - FIX showtree(n); */ 177 #endif 178 if (n == NEOF) { 179 if (Iflag == 0 || numeof >= 50) 180 break; 181 out2str("\nUse \"exit\" to leave shell.\n"); 182 numeof++; 183 } else if (n != NULL && nflag == 0) { 184 if (inter) { 185 INTOFF; 186 if (prevcmd) 187 freefunc(prevcmd); 188 prevcmd = curcmd; 189 curcmd = copyfunc(n); 190 INTON; 191 } 192 evaltree(n, 0); 193 #ifdef notdef 194 if (exitstatus) /*DEBUG*/ 195 outfmt(&errout, "Exit status 0x%X\n", exitstatus); 196 #endif 197 } 198 popstackmark(&smark); 199 } 200 popstackmark(&smark); /* unnecessary */ 201 } 202 203 204 205 /* 206 * Read /etc/profile or .profile. Return on error. 207 */ 208 209 STATIC void 210 read_profile(name) 211 char *name; 212 { 213 int fd; 214 215 INTOFF; 216 if ((fd = open(name, O_RDONLY)) >= 0) 217 setinputfd(fd, 1); 218 INTON; 219 if (fd < 0) 220 return; 221 cmdloop(0); 222 popfile(); 223 } 224 225 226 227 /* 228 * Read a file containing shell functions. 229 */ 230 231 void 232 readcmdfile(name) 233 char *name; 234 { 235 int fd; 236 237 INTOFF; 238 if ((fd = open(name, O_RDONLY)) >= 0) 239 setinputfd(fd, 1); 240 else 241 error("Can't open %s", name); 242 INTON; 243 cmdloop(0); 244 popfile(); 245 } 246 247 248 249 /* 250 * Take commands from a file. To be compatable we should do a path 251 * search for the file, but a path search doesn't make any sense. 252 */ 253 254 dotcmd(argc, argv) char **argv; { 255 exitstatus = 0; 256 if (argc >= 2) { /* That's what SVR2 does */ 257 setinputfile(argv[1], 1); 258 commandname = argv[1]; 259 cmdloop(0); 260 popfile(); 261 } 262 return exitstatus; 263 } 264 265 266 exitcmd(argc, argv) char **argv; { 267 if (argc > 1) 268 exitstatus = number(argv[1]); 269 exitshell(exitstatus); 270 } 271 272 273 lccmd(argc, argv) char **argv; { 274 if (argc > 1) { 275 defun(argv[1], prevcmd); 276 return 0; 277 } else { 278 INTOFF; 279 freefunc(curcmd); 280 curcmd = prevcmd; 281 prevcmd = NULL; 282 INTON; 283 evaltree(curcmd, 0); 284 return exitstatus; 285 } 286 } 287 288 289 290 #ifdef notdef 291 /* 292 * Should never be called. 293 */ 294 295 void 296 exit(exitstatus) { 297 _exit(exitstatus); 298 } 299 #endif 300