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