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.3 (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 (getuid() == geteuid() && getgid() == getegid()) { 135 if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { 136 state = 3; 137 read_profile(shinit); 138 } 139 } 140 state3: 141 state = 4; 142 if (minusc) { 143 evalstring(minusc); 144 } 145 if (sflag || minusc == NULL) { 146 state4: /* XXX ??? - why isn't this before the "if" statement */ 147 cmdloop(1); 148 } 149 #if PROFILE 150 monitor(0); 151 #endif 152 exitshell(exitstatus); 153 } 154 155 156 /* 157 * Read and execute commands. "Top" is nonzero for the top level command 158 * loop; it turns on prompting if the shell is interactive. 159 */ 160 161 void 162 cmdloop(top) { 163 union node *n; 164 struct stackmark smark; 165 int inter; 166 int numeof = 0; 167 168 TRACE(("cmdloop(%d) called\n", top)); 169 setstackmark(&smark); 170 for (;;) { 171 if (pendingsigs) 172 dotrap(); 173 inter = 0; 174 if (iflag && top) { 175 inter++; 176 showjobs(1); 177 chkmail(0); 178 flushout(&output); 179 } 180 n = parsecmd(inter); 181 /* showtree(n); DEBUG */ 182 if (n == NEOF) { 183 if (!top || numeof >= 50) 184 break; 185 if (!stoppedjobs()) { 186 if (!Iflag) 187 break; 188 out2str("\nUse \"exit\" to leave shell.\n"); 189 } 190 numeof++; 191 } else if (n != NULL && nflag == 0) { 192 job_warning = (job_warning == 2) ? 1 : 0; 193 numeof = 0; 194 evaltree(n, 0); 195 } 196 popstackmark(&smark); 197 } 198 popstackmark(&smark); /* unnecessary */ 199 } 200 201 202 203 /* 204 * Read /etc/profile or .profile. Return on error. 205 */ 206 207 STATIC void 208 read_profile(name) 209 char *name; 210 { 211 int fd; 212 213 INTOFF; 214 if ((fd = open(name, O_RDONLY)) >= 0) 215 setinputfd(fd, 1); 216 INTON; 217 if (fd < 0) 218 return; 219 cmdloop(0); 220 popfile(); 221 } 222 223 224 225 /* 226 * Read a file containing shell functions. 227 */ 228 229 void 230 readcmdfile(name) 231 char *name; 232 { 233 int fd; 234 235 INTOFF; 236 if ((fd = open(name, O_RDONLY)) >= 0) 237 setinputfd(fd, 1); 238 else 239 error("Can't open %s", name); 240 INTON; 241 cmdloop(0); 242 popfile(); 243 } 244 245 246 247 /* 248 * Take commands from a file. To be compatable we should do a path 249 * search for the file, which is necessary to find sub-commands. 250 */ 251 252 253 static char * 254 find_dot_file(basename) char *basename; { 255 static char localname[FILENAME_MAX+1]; 256 char *fullname; 257 char *path = pathval(); 258 struct stat statb; 259 260 /* don't try this for absolute or relative paths */ 261 if( strchr(basename, '/')) 262 return basename; 263 264 while ((fullname = padvance(&path, basename)) != NULL) { 265 strcpy(localname, fullname); 266 stunalloc(fullname); 267 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) 268 return localname; 269 } 270 return basename; 271 } 272 273 dotcmd(argc, argv) char **argv; { 274 exitstatus = 0; 275 if (argc >= 2) { /* That's what SVR2 does */ 276 char *fullname = find_dot_file(argv[1]); 277 setinputfile(fullname, 1); 278 commandname = fullname; 279 cmdloop(0); 280 popfile(); 281 } 282 return exitstatus; 283 } 284 285 286 exitcmd(argc, argv) char **argv; { 287 if (stoppedjobs()) 288 return; 289 if (argc > 1) 290 exitstatus = number(argv[1]); 291 exitshell(exitstatus); 292 } 293 294 295 #ifdef notdef 296 /* 297 * Should never be called. 298 */ 299 300 void 301 exit(exitstatus) { 302 _exit(exitstatus); 303 } 304 #endif 305