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