From c5bfa400448a14f4c8d4fc51dd56ea17176e1f84 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Sat, 14 Sep 2013 01:04:04 -0400 Subject: Support the -Orecurse option properly. In this mode we still collect all the output from a given target and dump it at once. However we don't treat recursive lines any differently from non-recursive lines. Also we don't print enter/leave messages after every dump. However we do ensure that we always print them once to stdout, so the parent make will collect it properly. --- vmsjobs.c | 729 +++++++++++++++++++++++++++++++------------------------------- 1 file changed, 362 insertions(+), 367 deletions(-) (limited to 'vmsjobs.c') diff --git a/vmsjobs.c b/vmsjobs.c index 8d30157..8bacc86 100644 --- a/vmsjobs.c +++ b/vmsjobs.c @@ -31,10 +31,10 @@ vmsWaitForChildren(int *status) while (1) { if (!vms_jobsefnmask) - { - *status = 0; - return; - } + { + *status = 0; + return; + } *status = sys$wflor (32, vms_jobsefnmask); } @@ -59,9 +59,9 @@ vms_redirect (struct dsc$descriptor_s *desc, char *fname, char *ibuf) { strcpy (fname, vmsify (fptr, 0)); if (strchr (fname, '.') == 0) - strcat (fname, "."); + strcat (fname, "."); } - desc->dsc$w_length = strlen(fname); + desc->dsc$w_length = strlen (fname); desc->dsc$a_pointer = fname; desc->dsc$b_dtype = DSC$K_DTYPE_T; desc->dsc$b_class = DSC$K_CLASS_S; @@ -74,7 +74,7 @@ vms_redirect (struct dsc$descriptor_s *desc, char *fname, char *ibuf) /* found apostrophe at (p-1) inc p until after closing apostrophe. - */ +*/ char * vms_handle_apos (char *p) @@ -86,25 +86,21 @@ vms_handle_apos (char *p) alast = 0; while (*p != 0) - { - if (*p == '"') - { - if (alast) - { - alast = 0; - p++; - } - else - { - p++; - if (strchr (SEPCHARS, *p)) - break; - alast = 1; - } - } + if (*p == '"') + if (alast) + { + alast = 0; + p++; + } else - p++; - } + { + p++; + if (strchr (SEPCHARS, *p)) + break; + alast = 1; + } + else + p++; return p; } @@ -118,100 +114,99 @@ static int ctrlYPressed= 0; int vmsHandleChildTerm(struct child *child) { - int status; - register struct child *lastc, *c; - int child_failed; + int status; + register struct child *lastc, *c; + int child_failed; - vms_jobsefnmask &= ~(1 << (child->efn - 32)); + vms_jobsefnmask &= ~(1 << (child->efn - 32)); - lib$free_ef(&child->efn); - if (child->comname) - { - if (!ISDB (DB_JOBS)&&!ctrlYPressed) - unlink (child->comname); - free (child->comname); - } + lib$free_ef (&child->efn); + if (child->comname) + { + if (!ISDB (DB_JOBS) && !ctrlYPressed) + unlink (child->comname); + free (child->comname); + } - (void) sigblock (fatal_signal_mask); + (void) sigblock (fatal_signal_mask); - child_failed = !(child->cstatus & 1 || ((child->cstatus & 7) == 0)); + child_failed = !(child->cstatus & 1 || ((child->cstatus & 7) == 0)); - /* Search for a child matching the deceased one. */ - lastc = 0; + /* Search for a child matching the deceased one. */ + lastc = 0; #if defined(RECURSIVEJOBS) /* I've had problems with recursive stuff and process handling */ - for (c = children; c != 0 && c != child; lastc = c, c = c->next) - ; + for (c = children; c != 0 && c != child; lastc = c, c = c->next) + ; #else - c = child; + c = child; #endif - if (child_failed && !c->noerror && !ignore_errors_flag) - { - /* The commands failed. Write an error message, - delete non-precious targets, and abort. */ - child_error (c, c->cstatus, 0, 0, 0); - c->file->update_status = us_failed; - delete_child_targets (c); - } - else - { - if (child_failed) - { - /* The commands failed, but we don't care. */ - child_error (c, c->cstatus, 0, 0, 1); - child_failed = 0; - } + if (child_failed && !c->noerror && !ignore_errors_flag) + { + /* The commands failed. Write an error message, + delete non-precious targets, and abort. */ + child_error (c, c->cstatus, 0, 0, 0); + c->file->update_status = us_failed; + delete_child_targets (c); + } + else + { + if (child_failed) + { + /* The commands failed, but we don't care. */ + child_error (c, c->cstatus, 0, 0, 1); + child_failed = 0; + } #if defined(RECURSIVEJOBS) /* I've had problems with recursive stuff and process handling */ - /* If there are more commands to run, try to start them. */ - start_job (c); - - switch (c->file->command_state) - { - case cs_running: - /* Successfully started. */ - break; - - case cs_finished: - if (c->file->update_status != us_success) { - /* We failed to start the commands. */ - delete_child_targets (c); - } - break; - - default: - error (NILF, _("internal error: '%s' command_state"), - c->file->name); - abort (); - break; - } + /* If there are more commands to run, try to start them. */ + start_job (c); + + switch (c->file->command_state) + { + case cs_running: + /* Successfully started. */ + break; + + case cs_finished: + if (c->file->update_status != us_success) + /* We failed to start the commands. */ + delete_child_targets (c); + break; + + default: + error (NILF, _("internal error: '%s' command_state"), + c->file->name); + abort (); + break; + } #endif /* RECURSIVEJOBS */ - } + } - /* Set the state flag to say the commands have finished. */ - c->file->command_state = cs_finished; - notice_finished_file (c->file); + /* Set the state flag to say the commands have finished. */ + c->file->command_state = cs_finished; + notice_finished_file (c->file); #if defined(RECURSIVEJOBS) /* I've had problems with recursive stuff and process handling */ - /* Remove the child from the chain and free it. */ - if (lastc == 0) - children = c->next; - else - lastc->next = c->next; - free_child (c); + /* Remove the child from the chain and free it. */ + if (lastc == 0) + children = c->next; + else + lastc->next = c->next; + free_child (c); #endif /* RECURSIVEJOBS */ - /* There is now another slot open. */ - if (job_slots_used > 0) - --job_slots_used; + /* There is now another slot open. */ + if (job_slots_used > 0) + --job_slots_used; - /* If the job failed, and the -k flag was not given, die. */ - if (child_failed && !keep_going_flag) - die (EXIT_FAILURE); + /* If the job failed, and the -k flag was not given, die. */ + if (child_failed && !keep_going_flag) + die (EXIT_FAILURE); - (void) sigsetmask (sigblock (0) & ~(fatal_signal_mask)); + (void) sigsetmask (sigblock (0) & ~(fatal_signal_mask)); - return 1; + return 1; } /* VMS: @@ -232,67 +227,71 @@ static unsigned short int chan= 0; static void reEnableAst(void) { - lib$enable_ctrl (&oldCtrlMask,0); + lib$enable_ctrl (&oldCtrlMask,0); } static int astYHandler (void) { - struct child *c; - for (c = children; c != 0; c = c->next) - sys$delprc (&c->pid, 0, 0); - ctrlYPressed= 1; - kill (getpid(),SIGQUIT); - return SS$_NORMAL; + struct child *c; + for (c = children; c != 0; c = c->next) + sys$delprc (&c->pid, 0, 0); + ctrlYPressed= 1; + kill (getpid(),SIGQUIT); + return SS$_NORMAL; } static void tryToSetupYAst(void) { - $DESCRIPTOR(inputDsc,"SYS$COMMAND"); - int status; - struct { - short int status, count; - int dvi; - } iosb; - unsigned short int loc_chan; - - setupYAstTried++; - - if (chan) - loc_chan= chan; - else { - status= sys$assign(&inputDsc,&loc_chan,0,0); - if (!(status&SS$_NORMAL)) { - lib$signal(status); - return; - } - } - status= sys$qiow (0, loc_chan, IO$_SETMODE|IO$M_CTRLYAST,&iosb,0,0, - astYHandler,0,0,0,0,0); - if (status==SS$_NORMAL) - status= iosb.status; - if (status!=SS$_NORMAL) { - if (!chan) - sys$dassgn(loc_chan); - if (status!=SS$_ILLIOFUNC && status!=SS$_NOPRIV) - lib$signal(status); - return; - } - - /* called from AST handler ? */ - if (setupYAstTried>1) - return; - if (atexit(reEnableAst)) - fprintf (stderr, - _("-warning, you may have to re-enable CTRL-Y handling from DCL.\n")); - status= lib$disable_ctrl (&ctrlMask, &oldCtrlMask); - if (!(status&SS$_NORMAL)) { - lib$signal(status); - return; - } - if (!chan) - chan = loc_chan; + $DESCRIPTOR(inputDsc,"SYS$COMMAND"); + int status; + struct { + short int status, count; + int dvi; + } iosb; + unsigned short int loc_chan; + + setupYAstTried++; + + if (chan) + loc_chan= chan; + else + { + status= sys$assign(&inputDsc,&loc_chan,0,0); + if (!(status&SS$_NORMAL)) + { + lib$signal(status); + return; + } + } + status= sys$qiow (0, loc_chan, IO$_SETMODE|IO$M_CTRLYAST,&iosb,0,0, + astYHandler,0,0,0,0,0); + if (status==SS$_NORMAL) + status= iosb.status; + if (status!=SS$_NORMAL) + { + if (!chan) + sys$dassgn(loc_chan); + if (status!=SS$_ILLIOFUNC && status!=SS$_NOPRIV) + lib$signal(status); + return; + } + + /* called from AST handler ? */ + if (setupYAstTried>1) + return; + if (atexit(reEnableAst)) + fprintf (stderr, + _("-warning, you may have to re-enable CTRL-Y handling from DCL.\n")); + status= lib$disable_ctrl (&ctrlMask, &oldCtrlMask); + if (!(status&SS$_NORMAL)) + { + lib$signal(status); + return; + } + if (!chan) + chan = loc_chan; } int @@ -349,63 +348,61 @@ child_execute_job (char *argv, struct child *child) continue; } switch (*p) - { - case '#': - *p-- = 0; - *q-- = 0; - break; - case '\\': - p++; - if (*p == '\n') - p++; - if (isspace ((unsigned char)*p)) - { - do { p++; } while (isspace ((unsigned char)*p)); - p--; - } - *q = *p; - break; - case '<': - p = vms_redirect (&ifiledsc, ifile, p); - *q = ' '; - have_redirection = 1; - break; - case '>': - have_redirection = 1; - if (*(p-1) == '2') - { - q--; - if (strncmp (p, ">&1", 3) == 0) - { - p += 3; - strcpy (efile, "sys$output"); - efiledsc.dsc$w_length = strlen(efile); - efiledsc.dsc$a_pointer = efile; - efiledsc.dsc$b_dtype = DSC$K_DTYPE_T; - efiledsc.dsc$b_class = DSC$K_CLASS_S; - } - else - { - p = vms_redirect (&efiledsc, efile, p); - } - } - else - { - if (*(p+1) == '>') - { - have_append = 1; - p += 1; - } - p = vms_redirect (&ofiledsc, ofile, p); - } - *q = ' '; - break; - case '\n': - have_newline = 1; - default: - *q = *p; - break; - } + { + case '#': + *p-- = 0; + *q-- = 0; + break; + case '\\': + p++; + if (*p == '\n') + p++; + if (isspace ((unsigned char)*p)) + { + do { p++; } while (isspace ((unsigned char)*p)); + p--; + } + *q = *p; + break; + case '<': + p = vms_redirect (&ifiledsc, ifile, p); + *q = ' '; + have_redirection = 1; + break; + case '>': + have_redirection = 1; + if (*(p-1) == '2') + { + q--; + if (strncmp (p, ">&1", 3) == 0) + { + p += 3; + strcpy (efile, "sys$output"); + efiledsc.dsc$w_length = strlen(efile); + efiledsc.dsc$a_pointer = efile; + efiledsc.dsc$b_dtype = DSC$K_DTYPE_T; + efiledsc.dsc$b_class = DSC$K_CLASS_S; + } + else + p = vms_redirect (&efiledsc, efile, p); + } + else + { + if (*(p+1) == '>') + { + have_append = 1; + p += 1; + } + p = vms_redirect (&ofiledsc, ofile, p); + } + *q = ' '; + break; + case '\n': + have_newline = 1; + default: + *q = *p; + break; + } } *q = *p; while (isspace ((unsigned char)*--q)) @@ -422,55 +419,55 @@ child_execute_job (char *argv, struct child *child) p = cmd + 8; if ((*(p) == 'c') - && (*(p+1) == 'd') - && ((*(p+2) == ' ') || (*(p+2) == '\t'))) - { - p += 3; - while ((*p == ' ') || (*p == '\t')) - p++; - DB (DB_JOBS, (_("BUILTIN CD %s\n"), p)); - if (chdir (p)) - return 0; - else - return 1; - } + && (*(p+1) == 'd') + && ((*(p+2) == ' ') || (*(p+2) == '\t'))) + { + p += 3; + while ((*p == ' ') || (*p == '\t')) + p++; + DB (DB_JOBS, (_("BUILTIN CD %s\n"), p)); + if (chdir (p)) + return 0; + else + return 1; + } else if ((*(p) == 'r') - && (*(p+1) == 'm') - && ((*(p+2) == ' ') || (*(p+2) == '\t'))) - { - int in_arg; - - /* rm */ - p += 3; - while ((*p == ' ') || (*p == '\t')) - p++; - in_arg = 1; - - DB (DB_JOBS, (_("BUILTIN RM %s\n"), p)); - while (*p) - { - switch (*p) - { - case ' ': - case '\t': - if (in_arg) - { - *p++ = ';'; - in_arg = 0; - } - break; - default: - break; - } - p++; - } - } + && (*(p+1) == 'm') + && ((*(p+2) == ' ') || (*(p+2) == '\t'))) + { + int in_arg; + + /* rm */ + p += 3; + while ((*p == ' ') || (*p == '\t')) + p++; + in_arg = 1; + + DB (DB_JOBS, (_("BUILTIN RM %s\n"), p)); + while (*p) + { + switch (*p) + { + case ' ': + case '\t': + if (in_arg) + { + *p++ = ';'; + in_arg = 0; + } + break; + default: + break; + } + p++; + } + } else - { - printf(_("Unknown builtin command '%s'\n"), cmd); - fflush(stdout); - return 0; - } + { + printf (_("Unknown builtin command '%s'\n"), cmd); + fflush (stdout); + return 0; + } } /* Create a *.com file if either the command is too long for @@ -485,56 +482,54 @@ child_execute_job (char *argv, struct child *child) FILE *outfile; char c; char *sep; - int alevel = 0; /* apostrophe level */ + int alevel = 0; /* apostrophe level */ if (strlen (cmd) == 0) - { - printf (_("Error, empty command\n")); - fflush (stdout); - return 0; - } + { + printf (_("Error, empty command\n")); + fflush (stdout); + return 0; + } - outfile = open_tmpfile (&child->comname, "sys$scratch:CMDXXXXXX.COM"); + outfile = output_tmpfile (&child->comname, "sys$scratch:CMDXXXXXX.COM"); if (outfile == 0) - pfatal_with_name (_("fopen (temporary file)")); + pfatal_with_name (_("fopen (temporary file)")); comnamelen = strlen (child->comname); if (ifile[0]) - { - fprintf (outfile, "$ assign/user %s sys$input\n", ifile); + { + fprintf (outfile, "$ assign/user %s sys$input\n", ifile); DB (DB_JOBS, (_("Redirected input from %s\n"), ifile)); - ifiledsc.dsc$w_length = 0; - } + ifiledsc.dsc$w_length = 0; + } if (efile[0]) - { - fprintf (outfile, "$ define sys$error %s\n", efile); + { + fprintf (outfile, "$ define sys$error %s\n", efile); DB (DB_JOBS, (_("Redirected error to %s\n"), efile)); - efiledsc.dsc$w_length = 0; - } + efiledsc.dsc$w_length = 0; + } if (ofile[0]) - { - if (have_append) - { - fprintf (outfile, "$ set noon\n"); - fprintf (outfile, "$ define sys$output %.*s\n", comnamelen-3, child->comname); - DB (DB_JOBS, (_("Append output to %s\n"), ofile)); - ofiledsc.dsc$w_length = 0; - } - else - { - fprintf (outfile, "$ define sys$output %s\n", ofile); - DB (DB_JOBS, (_("Redirected output to %s\n"), ofile)); - ofiledsc.dsc$w_length = 0; - } - } + if (have_append) + { + fprintf (outfile, "$ set noon\n"); + fprintf (outfile, "$ define sys$output %.*s\n", comnamelen-3, child->comname); + DB (DB_JOBS, (_("Append output to %s\n"), ofile)); + ofiledsc.dsc$w_length = 0; + } + else + { + fprintf (outfile, "$ define sys$output %s\n", ofile); + DB (DB_JOBS, (_("Redirected output to %s\n"), ofile)); + ofiledsc.dsc$w_length = 0; + } p = sep = q = cmd; for (c = '\n'; c; c = *q++) - { - switch (c) - { + { + switch (c) + { case '\n': /* At a newline, skip any whitespace around a leading $ from the command and issue exactly one $ into the DCL. */ @@ -551,8 +546,8 @@ child_execute_job (char *argv, struct child *child) p = sep = q; break; - /* Nice places for line breaks are after strings, after - comma or space and before slash. */ + /* Nice places for line breaks are after strings, after + comma or space and before slash. */ case '"': q = vms_handle_apos (q); sep = q; @@ -567,25 +562,25 @@ child_execute_job (char *argv, struct child *child) break; default: break; - } - if (sep - p > 78) - { - /* Enough stuff for a line. */ - fwrite (p, 1, sep - p, outfile); - p = sep; - if (*sep) - { - /* The command continues. */ - fputc ('-', outfile); - } - fputc ('\n', outfile); - } - } + } + if (sep - p > 78) + { + /* Enough stuff for a line. */ + fwrite (p, 1, sep - p, outfile); + p = sep; + if (*sep) + { + /* The command continues. */ + fputc ('-', outfile); + } + fputc ('\n', outfile); + } + } if (*p) { fwrite (p, 1, --q - p, outfile); - fputc ('\n', outfile); + fputc ('\n', outfile); } if (have_append) @@ -630,66 +625,66 @@ child_execute_job (char *argv, struct child *child) vms_jobsefnmask |= (1 << (child->efn - 32)); -/* - LIB$SPAWN [command-string] - [,input-file] - [,output-file] - [,flags] - [,process-name] - [,process-id] [,completion-status-address] [,byte-integer-event-flag-num] - [,AST-address] [,varying-AST-argument] - [,prompt-string] [,cli] [,table] -*/ + /* + LIB$SPAWN [command-string] + [,input-file] + [,output-file] + [,flags] + [,process-name] + [,process-id] [,completion-status-address] [,byte-integer-event-flag-num] + [,AST-address] [,varying-AST-argument] + [,prompt-string] [,cli] [,table] + */ #ifndef DONTWAITFORCHILD -/* - * Code to make ctrl+c and ctrl+y working. - * The problem starts with the synchronous case where after lib$spawn is - * called any input will go to the child. But with input re-directed, - * both control characters won't make it to any of the programs, neither - * the spawning nor to the spawned one. Hence the caller needs to spawn - * with CLI$M_NOWAIT to NOT give up the input focus. A sys$waitfr - * has to follow to simulate the wanted synchronous behaviour. - * The next problem is ctrl+y which isn't caught by the crtl and - * therefore isn't converted to SIGQUIT (for a signal handler which is - * already established). The only way to catch ctrl+y, is an AST - * assigned to the input channel. But ctrl+y handling of DCL needs to be - * disabled, otherwise it will handle it. Not to mention the previous - * ctrl+y handling of DCL needs to be re-established before make exits. - * One more: At the time of LIB$SPAWN signals are blocked. SIGQUIT will - * make it to the signal handler after the child "normally" terminates. - * This isn't enough. It seems reasonable for simple command lines like - * a 'cc foobar.c' spawned in a subprocess but it is unacceptable for - * spawning make. Therefore we need to abort the process in the AST. - * - * Prior to the spawn it is checked if an AST is already set up for - * ctrl+y, if not one is set up for a channel to SYS$COMMAND. In general - * this will work except if make is run in a batch environment, but there - * nobody can press ctrl+y. During the setup the DCL handling of ctrl+y - * is disabled and an exit handler is established to re-enable it. - * If the user interrupts with ctrl+y, the assigned AST will fire, force - * an abort to the subprocess and signal SIGQUIT, which will be caught by - * the already established handler and will bring us back to common code. - * After the spawn (now /nowait) a sys$waitfr simulates the /wait and - * enables the ctrl+y be delivered to this code. And the ctrl+c too, - * which the crtl converts to SIGINT and which is caught by the common - * signal handler. Because signals were blocked before entering this code - * sys$waitfr will always complete and the SIGQUIT will be processed after - * it (after termination of the current block, somewhere in common code). - * And SIGINT too will be delayed. That is ctrl+c can only abort when the - * current command completes. Anyway it's better than nothing :-) - */ + /* + * Code to make ctrl+c and ctrl+y working. + * The problem starts with the synchronous case where after lib$spawn is + * called any input will go to the child. But with input re-directed, + * both control characters won't make it to any of the programs, neither + * the spawning nor to the spawned one. Hence the caller needs to spawn + * with CLI$M_NOWAIT to NOT give up the input focus. A sys$waitfr + * has to follow to simulate the wanted synchronous behaviour. + * The next problem is ctrl+y which isn't caught by the crtl and + * therefore isn't converted to SIGQUIT (for a signal handler which is + * already established). The only way to catch ctrl+y, is an AST + * assigned to the input channel. But ctrl+y handling of DCL needs to be + * disabled, otherwise it will handle it. Not to mention the previous + * ctrl+y handling of DCL needs to be re-established before make exits. + * One more: At the time of LIB$SPAWN signals are blocked. SIGQUIT will + * make it to the signal handler after the child "normally" terminates. + * This isn't enough. It seems reasonable for simple command lines like + * a 'cc foobar.c' spawned in a subprocess but it is unacceptable for + * spawning make. Therefore we need to abort the process in the AST. + * + * Prior to the spawn it is checked if an AST is already set up for + * ctrl+y, if not one is set up for a channel to SYS$COMMAND. In general + * this will work except if make is run in a batch environment, but there + * nobody can press ctrl+y. During the setup the DCL handling of ctrl+y + * is disabled and an exit handler is established to re-enable it. + * If the user interrupts with ctrl+y, the assigned AST will fire, force + * an abort to the subprocess and signal SIGQUIT, which will be caught by + * the already established handler and will bring us back to common code. + * After the spawn (now /nowait) a sys$waitfr simulates the /wait and + * enables the ctrl+y be delivered to this code. And the ctrl+c too, + * which the crtl converts to SIGINT and which is caught by the common + * signal handler. Because signals were blocked before entering this code + * sys$waitfr will always complete and the SIGQUIT will be processed after + * it (after termination of the current block, somewhere in common code). + * And SIGINT too will be delayed. That is ctrl+c can only abort when the + * current command completes. Anyway it's better than nothing :-) + */ if (!setupYAstTried) tryToSetupYAst(); - status = lib$spawn (&cmddsc, /* cmd-string */ - (ifiledsc.dsc$w_length == 0)?0:&ifiledsc, /* input-file */ - (ofiledsc.dsc$w_length == 0)?0:&ofiledsc, /* output-file */ - &spflags, /* flags */ - &pnamedsc, /* proc name */ - &child->pid, &child->cstatus, &child->efn, - 0, 0, - 0, 0, 0); + status = lib$spawn (&cmddsc, /* cmd-string */ + (ifiledsc.dsc$w_length == 0)?0:&ifiledsc, /* input-file */ + (ofiledsc.dsc$w_length == 0)?0:&ofiledsc, /* output-file */ + &spflags, /* flags */ + &pnamedsc, /* proc name */ + &child->pid, &child->cstatus, &child->efn, + 0, 0, + 0, 0, 0); if (status & 1) { status= sys$waitfr (child->efn); @@ -697,13 +692,13 @@ child_execute_job (char *argv, struct child *child) } #else status = lib$spawn (&cmddsc, - (ifiledsc.dsc$w_length == 0)?0:&ifiledsc, - (ofiledsc.dsc$w_length == 0)?0:&ofiledsc, - &spflags, - &pnamedsc, - &child->pid, &child->cstatus, &child->efn, - vmsHandleChildTerm, child, - 0, 0, 0); + (ifiledsc.dsc$w_length == 0)?0:&ifiledsc, + (ofiledsc.dsc$w_length == 0)?0:&ofiledsc, + &spflags, + &pnamedsc, + &child->pid, &child->cstatus, &child->efn, + vmsHandleChildTerm, child, + 0, 0, 0); #endif if (!(status & 1)) -- cgit v1.2.3