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