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