walk.c (10018B)
1 /* walk.c: walks the parse tree. */ 2 3 #include "rc.h" 4 5 #include <sys/stat.h> 6 #include <signal.h> 7 #include <setjmp.h> 8 #include <termios.h> 9 #include <unistd.h> 10 11 #ifdef __OpenBSD__ 12 #include <sys/wait.h> 13 #endif 14 15 #include "jbwrap.h" 16 17 /* 18 global which indicates whether rc is executing a test; 19 used by rc -e so that if (false) does not exit. 20 */ 21 bool cond = FALSE; 22 23 static bool haspreredir(Node *); 24 static bool isallpre(Node *); 25 static bool dofork(bool); 26 static void dopipe(Node *); 27 static void loop_body(Node* n); 28 29 enum if_state { if_false, if_true, if_nothing }; 30 enum if_state if_last = if_nothing; 31 32 /* Tail-recursive version of walk() */ 33 34 #define WALK(x, y) { n = x; parent = y; goto top; } 35 36 /* walk the parse-tree. "obvious". */ 37 38 extern bool walk(Node *n, bool parent) { 39 top: sigchk(); 40 if (n == NULL) { 41 if (!parent) 42 exit(0); 43 set(TRUE); 44 return TRUE; 45 } 46 switch (n->type) { 47 case nArgs: case nBackq: case nConcat: case nCount: 48 case nFlat: case nLappend: case nRedir: case nVar: 49 case nVarsub: case nWord: 50 exec(glob(glom(n)), parent); /* simple command */ 51 break; 52 case nBody: 53 walk(n->u[0].p, TRUE); 54 WALK(n->u[1].p, parent); 55 /* WALK doesn't fall through */ 56 case nNowait: { 57 int pid; 58 if ((pid = rc_fork()) == 0) { 59 #if defined(RC_JOB) && defined(SIGTTOU) && defined(SIGTTIN) && defined(SIGTSTP) 60 setsigdefaults(FALSE); 61 rc_signal(SIGTTOU, SIG_IGN); /* Berkeleyized version: put it in a new pgroup. */ 62 rc_signal(SIGTTIN, SIG_IGN); 63 rc_signal(SIGTSTP, SIG_IGN); 64 setpgid(0, getpid()); 65 #else 66 setsigdefaults(TRUE); /* ignore SIGINT, SIGQUIT, SIGTERM */ 67 #endif 68 mvfd(rc_open("/dev/null", rFrom), 0); 69 walk(n->u[0].p, FALSE); 70 exit(getstatus()); 71 } 72 if (interactive) 73 fprint(2, "%d\n", pid); 74 varassign("apid", word(nprint("%d", pid), NULL), FALSE); 75 redirq = NULL; /* kill pre-redir queue */ 76 break; 77 } 78 case nAndalso: { 79 bool oldcond = cond; 80 cond = TRUE; 81 if (walk(n->u[0].p, TRUE)) { 82 cond = oldcond; 83 WALK(n->u[1].p, parent); 84 } else 85 cond = oldcond; 86 break; 87 } 88 case nOrelse: { 89 bool oldcond = cond; 90 cond = TRUE; 91 if (!walk(n->u[0].p, TRUE)) { 92 cond = oldcond; 93 WALK(n->u[1].p, parent); 94 } else 95 cond = oldcond; 96 break; 97 } 98 case nBang: 99 set(!walk(n->u[0].p, TRUE)); 100 break; 101 case nIf: { 102 bool oldcond = cond; 103 enum if_state if_this; 104 Node *true_cmd = n->u[1].p, *false_cmd = NULL; 105 if (true_cmd != NULL && true_cmd->type == nElse) { 106 false_cmd = true_cmd->u[1].p; 107 true_cmd = true_cmd->u[0].p; 108 } 109 cond = TRUE; 110 if_this = (enum if_state)walk(n->u[0].p, TRUE); 111 cond = oldcond; 112 if (if_last == if_nothing) if_last = if_this; 113 walk(if_this ? true_cmd : false_cmd, parent); 114 break; 115 } 116 case nIfnot: { 117 if (if_last == if_nothing) 118 rc_error("`if not' must follow `if'"); 119 if (if_last == if_false) 120 walk(n->u[0].p, TRUE); 121 if_last = if_nothing; 122 break; 123 } 124 case nWhile: { 125 Jbwrap break_jb; 126 Edata break_data; 127 Estack break_stack; 128 bool testtrue; 129 const bool oldcond = cond; 130 cond = TRUE; 131 if (!walk(n->u[0].p, TRUE)) { /* prevent spurious breaks inside test */ 132 cond = oldcond; 133 break; 134 } 135 cond = oldcond; 136 if (sigsetjmp(break_jb.j, 1)) 137 break; 138 break_data.jb = &break_jb; 139 except(eBreak, break_data, &break_stack); 140 141 cond = oldcond; 142 do { 143 Edata iter_data; 144 Estack iter_stack; 145 iter_data.b = newblock(); 146 except(eArena, iter_data, &iter_stack); 147 loop_body(n->u[1].p); 148 cond = TRUE; 149 testtrue = walk(n->u[0].p, TRUE); 150 cond = oldcond; 151 unexcept(eArena); 152 } while (testtrue); 153 cond = oldcond; 154 unexcept(eBreak); 155 break; 156 } 157 case nForin: { 158 List *l, *var = glom(n->u[0].p); 159 Jbwrap break_jb; 160 Edata break_data; 161 Estack break_stack; 162 if (sigsetjmp(break_jb.j, 1)) 163 break; 164 break_data.jb = &break_jb; 165 except(eBreak, break_data, &break_stack); 166 167 for (l = listcpy(glob(glom(n->u[1].p)), nalloc); l != NULL; l = l->n) { 168 Edata iter_data; 169 Estack iter_stack; 170 assign(var, word(l->w, NULL), FALSE); 171 iter_data.b = newblock(); 172 except(eArena, iter_data, &iter_stack); 173 loop_body(n->u[2].p); 174 unexcept(eArena); 175 } 176 unexcept(eBreak); 177 break; 178 } 179 case nSubshell: 180 if (dofork(TRUE)) { 181 setsigdefaults(FALSE); 182 walk(n->u[0].p, FALSE); 183 rc_exit(getstatus()); 184 } 185 break; 186 case nAssign: 187 if (n->u[0].p == NULL) 188 rc_error("null variable name"); 189 assign(glom(n->u[0].p), glob(glom(n->u[1].p)), FALSE); 190 set(TRUE); 191 break; 192 case nPipe: 193 dopipe(n); 194 break; 195 case nNewfn: { 196 List *l = glom(n->u[0].p); 197 if (l == NULL) 198 rc_error("null function name"); 199 while (l != NULL) { 200 if (dashex) 201 prettyprint_fn(2, l->w, n->u[1].p); 202 fnassign(l->w, n->u[1].p); 203 l = l->n; 204 } 205 set(TRUE); 206 break; 207 } 208 case nRmfn: { 209 List *l = glom(n->u[0].p); 210 while (l != NULL) { 211 if (dashex) 212 fprint(2, "fn %S\n", l->w); 213 fnrm(l->w); 214 l = l->n; 215 } 216 set(TRUE); 217 break; 218 } 219 case nDup: 220 redirq = NULL; 221 break; /* Null command */ 222 case nMatch: { 223 List *a = glob(glom(n->u[0].p)), *b = glom(n->u[1].p); 224 if (dashex) 225 fprint(2, (a != NULL && a->n != NULL) ? "~ (%L) %L\n" : "~ %L %L\n", a, " ", b, " "); 226 set(lmatch(a, b)); 227 break; 228 } 229 case nSwitch: { 230 List *v = glom(n->u[0].p); 231 while (1) { 232 do { 233 n = n->u[1].p; 234 if (n == NULL) 235 return istrue(); 236 } while (n->u[0].p == NULL || n->u[0].p->type != nCase); 237 if (lmatch(v, glom(n->u[0].p->u[0].p))) { 238 for (n = n->u[1].p; n != NULL && (n->u[0].p == NULL || n->u[0].p->type != nCase); n = n->u[1].p) 239 walk(n->u[0].p, TRUE); 240 break; 241 } 242 } 243 break; 244 } 245 case nPre: { 246 List *v; 247 if (n->u[0].p->type == nRedir || n->u[0].p->type == nDup) { 248 if (redirq == NULL && !dofork(parent)) /* subshell on first preredir */ 249 break; 250 setsigdefaults(FALSE); 251 qredir(n->u[0].p); 252 if (!haspreredir(n->u[1].p)) 253 doredirs(); /* no more preredirs, empty queue */ 254 walk(n->u[1].p, FALSE); 255 rc_exit(getstatus()); 256 /* NOTREACHED */ 257 } else if (n->u[0].p->type == nAssign) { 258 if (isallpre(n->u[1].p)) { 259 walk(n->u[0].p, TRUE); 260 WALK(n->u[1].p, parent); 261 } else { 262 Estack e; 263 Edata var; 264 v = glom(n->u[0].p->u[0].p); 265 assign(v, glob(glom(n->u[0].p->u[1].p)), TRUE); 266 var.name = v->w; 267 except(eVarstack, var, &e); 268 walk(n->u[1].p, parent); 269 varrm(v->w, TRUE); 270 unexcept(eVarstack); 271 } 272 } else 273 panic("unexpected node in preredir section of walk"); 274 break; 275 } 276 case nBrace: 277 if (n->u[1].p == NULL) { 278 WALK(n->u[0].p, parent); 279 } else if (dofork(parent)) { 280 setsigdefaults(FALSE); 281 walk(n->u[1].p, TRUE); /* Do redirections */ 282 redirq = NULL; /* Reset redirection queue */ 283 walk(n->u[0].p, FALSE); /* Do commands */ 284 rc_exit(getstatus()); 285 /* NOTREACHED */ 286 } 287 break; 288 case nEpilog: 289 qredir(n->u[0].p); 290 if (n->u[1].p != NULL) { 291 WALK(n->u[1].p, parent); /* Do more redirections. */ 292 } else { 293 doredirs(); /* Okay, we hit the bottom. */ 294 } 295 break; 296 case nNmpipe: 297 rc_error("named pipes cannot be executed as commands"); 298 /* NOTREACHED */ 299 default: 300 panic("unknown node in walk"); 301 /* NOTREACHED */ 302 } 303 return istrue(); 304 } 305 306 /* checks to see whether there are any pre-redirections left in the tree */ 307 308 static bool haspreredir(Node *n) { 309 while (n != NULL && n->type == nPre) { 310 if (n->u[0].p->type == nDup || n->u[0].p->type == nRedir) 311 return TRUE; 312 n = n->u[1].p; 313 } 314 return FALSE; 315 } 316 317 /* checks to see whether a subtree is all pre-command directives, i.e., assignments and redirs only */ 318 319 static bool isallpre(Node *n) { 320 while (n != NULL && n->type == nPre) 321 n = n->u[1].p; 322 return n == NULL || n->type == nRedir || n->type == nAssign || n->type == nDup; 323 } 324 325 /* 326 A code-saver. Forks, child returns (for further processing in walk()), and the parent 327 waits for the child to finish, setting $status appropriately. 328 */ 329 330 static bool dofork(bool parent) { 331 int pid, sp; 332 struct termios t; 333 334 if (interactive) 335 tcgetattr(0, &t); 336 if (!parent || (pid = rc_fork()) == 0) 337 return TRUE; 338 redirq = NULL; /* clear out the pre-redirection queue in the parent */ 339 rc_wait4(pid, &sp, TRUE); 340 if (interactive && WIFSIGNALED(sp)) 341 tcsetattr(0, TCSANOW, &t); 342 setstatus(-1, sp); 343 sigchk(); 344 return FALSE; 345 } 346 347 static void dopipe(Node *n) { 348 int i, j, sp, pid, fd_prev, fd_out, pids[512], stats[512], p[2]; 349 bool intr; 350 Node *r; 351 struct termios t; 352 353 if (interactive) 354 tcgetattr(0, &t); 355 fd_prev = fd_out = 1; 356 for (r = n, i = 0; r != NULL && r->type == nPipe; r = r->u[2].p, i++) { 357 if (i > 500) /* the only hard-wired limit in rc? */ 358 rc_error("pipe too long"); 359 if (pipe(p) < 0) { 360 uerror("pipe"); 361 rc_error(NULL); 362 } 363 if ((pid = rc_fork()) == 0) { 364 setsigdefaults(FALSE); 365 redirq = NULL; /* clear preredir queue */ 366 mvfd(p[0], r->u[1].i); 367 if (fd_prev != 1) 368 mvfd(fd_prev, fd_out); 369 close(p[1]); 370 walk(r->u[3].p, FALSE); 371 exit(getstatus()); 372 } 373 if (fd_prev != 1) 374 close(fd_prev); /* parent must close all pipe fd's */ 375 pids[i] = pid; 376 fd_prev = p[1]; 377 fd_out = r->u[0].i; 378 close(p[0]); 379 } 380 if ((pid = rc_fork()) == 0) { 381 setsigdefaults(FALSE); 382 mvfd(fd_prev, fd_out); 383 walk(r, FALSE); 384 exit(getstatus()); 385 /* NOTREACHED */ 386 } 387 redirq = NULL; /* clear preredir queue */ 388 close(fd_prev); 389 pids[i++] = pid; 390 391 /* collect statuses */ 392 393 intr = FALSE; 394 for (j = 0; j < i; j++) { 395 rc_wait4(pids[j], &sp, TRUE); 396 stats[j] = sp; 397 intr |= WIFSIGNALED(sp); 398 } 399 if (interactive && intr) 400 tcsetattr(0, TCSANOW, &t); 401 setpipestatus(stats, i); 402 sigchk(); 403 } 404 405 /* From http://en.cppreference.com/w/c/program/setjmp 406 * According to the C standard setjmp() must appear only in the following 4 constructs: 407 * 1. switch (setjmp(args)) {statements} 408 * 2. if (setjmp(args) == Const) {statements} with any of 409 * operators: ==, !=, <, >, <=, >= 410 * 3. while (! setjmp(args)) {statements} 411 * 4. setjmp(args); 412 */ 413 static void loop_body(Node* nd) 414 { 415 Node *volatile n = nd; 416 Jbwrap cont_jb; 417 Edata cont_data; 418 Estack cont_stack; 419 420 if (sigsetjmp(cont_jb.j, 1) == 0) { 421 cont_data.jb = &cont_jb; 422 except(eContinue, cont_data, &cont_stack); 423 walk(n, TRUE); 424 unexcept(eContinue); 425 } 426 }