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.4 (Berkeley) 05/04/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 == EXSHELLPROC) { 91 rootpid = getpid(); 92 rootshell = 1; 93 minusc = NULL; 94 state = 3; 95 } else if (state == 0 || iflag == 0 || ! rootshell) 96 exitshell(2); 97 reset(); 98 if (exception == EXINT 99 #if ATTY 100 && (! attyset() || equal(termval(), "emacs")) 101 #endif 102 ) { 103 out2c('\n'); 104 flushout(&errout); 105 } 106 popstackmark(&smark); 107 FORCEINTON; /* enable interrupts */ 108 if (state == 1) 109 goto state1; 110 else if (state == 2) 111 goto state2; 112 else if (state == 3) 113 goto state3; 114 else 115 goto state4; 116 } 117 handler = &jmploc; 118 #ifdef DEBUG 119 opentrace(); 120 trputs("Shell args: "); trargs(argv); 121 #endif 122 rootpid = getpid(); 123 rootshell = 1; 124 init(); 125 setstackmark(&smark); 126 procargs(argc, argv); 127 if (argv[0] && argv[0][0] == '-') { 128 state = 1; 129 read_profile("/etc/profile"); 130 state1: 131 state = 2; 132 read_profile(".profile"); 133 } 134 state2: 135 state = 3; 136 if (getuid() == geteuid() && getgid() == getegid()) { 137 if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { 138 state = 3; 139 read_profile(shinit); 140 } 141 } 142 state3: 143 state = 4; 144 if (minusc) { 145 evalstring(minusc); 146 } 147 if (sflag || minusc == NULL) { 148 state4: /* XXX ??? - why isn't this before the "if" statement */ 149 cmdloop(1); 150 } 151 #if PROFILE 152 monitor(0); 153 #endif 154 exitshell(exitstatus); 155 /*NOTREACHED*/ 156 return 0; 157 } 158 159 160 /* 161 * Read and execute commands. "Top" is nonzero for the top level command 162 * loop; it turns on prompting if the shell is interactive. 163 */ 164 165 void 166 cmdloop(top) 167 int top; 168 { 169 union node *n; 170 struct stackmark smark; 171 int inter; 172 int numeof = 0; 173 174 TRACE(("cmdloop(%d) called\n", top)); 175 setstackmark(&smark); 176 for (;;) { 177 if (pendingsigs) 178 dotrap(); 179 inter = 0; 180 if (iflag && top) { 181 inter++; 182 showjobs(1); 183 chkmail(0); 184 flushout(&output); 185 } 186 n = parsecmd(inter); 187 /* showtree(n); DEBUG */ 188 if (n == NEOF) { 189 if (!top || numeof >= 50) 190 break; 191 if (!stoppedjobs()) { 192 if (!Iflag) 193 break; 194 out2str("\nUse \"exit\" to leave shell.\n"); 195 } 196 numeof++; 197 } else if (n != NULL && nflag == 0) { 198 job_warning = (job_warning == 2) ? 1 : 0; 199 numeof = 0; 200 evaltree(n, 0); 201 } 202 popstackmark(&smark); 203 } 204 popstackmark(&smark); /* unnecessary */ 205 } 206 207 208 209 /* 210 * Read /etc/profile or .profile. Return on error. 211 */ 212 213 STATIC void 214 read_profile(name) 215 char *name; 216 { 217 int fd; 218 219 INTOFF; 220 if ((fd = open(name, O_RDONLY)) >= 0) 221 setinputfd(fd, 1); 222 INTON; 223 if (fd < 0) 224 return; 225 cmdloop(0); 226 popfile(); 227 } 228 229 230 231 /* 232 * Read a file containing shell functions. 233 */ 234 235 void 236 readcmdfile(name) 237 char *name; 238 { 239 int fd; 240 241 INTOFF; 242 if ((fd = open(name, O_RDONLY)) >= 0) 243 setinputfd(fd, 1); 244 else 245 error("Can't open %s", name); 246 INTON; 247 cmdloop(0); 248 popfile(); 249 } 250 251 252 253 /* 254 * Take commands from a file. To be compatable we should do a path 255 * search for the file, which is necessary to find sub-commands. 256 */ 257 258 259 STATIC char * 260 find_dot_file(basename) 261 char *basename; 262 { 263 static char localname[FILENAME_MAX+1]; 264 char *fullname; 265 char *path = pathval(); 266 struct stat statb; 267 268 /* don't try this for absolute or relative paths */ 269 if( strchr(basename, '/')) 270 return basename; 271 272 while ((fullname = padvance(&path, basename)) != NULL) { 273 strcpy(localname, fullname); 274 stunalloc(fullname); 275 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) 276 return localname; 277 } 278 return basename; 279 } 280 281 int 282 dotcmd(argc, argv) 283 int argc; 284 char **argv; 285 { 286 exitstatus = 0; 287 if (argc >= 2) { /* That's what SVR2 does */ 288 char *fullname = find_dot_file(argv[1]); 289 setinputfile(fullname, 1); 290 commandname = fullname; 291 cmdloop(0); 292 popfile(); 293 } 294 return exitstatus; 295 } 296 297 298 int 299 exitcmd(argc, argv) 300 int argc; 301 char **argv; 302 { 303 if (stoppedjobs()) 304 return 0; 305 if (argc > 1) 306 exitstatus = number(argv[1]); 307 exitshell(exitstatus); 308 /*NOTREACHED*/ 309 return 0; 310 } 311 312 313 #ifdef notdef 314 /* 315 * Should never be called. 316 */ 317 318 void 319 exit(exitstatus) { 320 _exit(exitstatus); 321 } 322 #endif 323