From c5bfa400448a14f4c8d4fc51dd56ea17176e1f84 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Sat, 14 Sep 2013 01:04:04 -0400 Subject: [PATCH] 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. --- ChangeLog | 16 + function.c | 2 +- job.c | 4 +- main.c | 55 +-- makeint.h | 3 - misc.c | 96 +--- output.c | 274 +++++++---- output.h | 12 +- tests/ChangeLog | 4 + tests/scripts/features/output-sync | 2 - vmsjobs.c | 713 ++++++++++++++--------------- 11 files changed, 592 insertions(+), 589 deletions(-) diff --git a/ChangeLog b/ChangeLog index c4319acc..14a6ce4f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2013-09-14 Paul Smith + + * misc.c (set_append_mode, open_tmpfd, open_tmpfile): Move to output.c. + * misc.h: Ditto. + * output.h: Ditto. + * main.c (main): Move stdio init into output.c:output_init(). + Change open_tmpfile() to output_tmpfile(). + * output.c: Rename open_*() to output_*(). set_append_mode() and + open_tmpfd() are static. + (_outputs, log_working_directory): Accept a struct output and + print to that rather than the global context. + (output_dump): In recurse mode print enter/leave once for the + whole makefile. + (output_init): Initialize this processes stdio as well as child's. + * vmsjobs.c: Reformat to be closer to convention. + 2013-09-12 Paul Smith Rework output to handle synchronization and directory logging more diff --git a/function.c b/function.c index 3379c904..40e10981 100644 --- a/function.c +++ b/function.c @@ -1846,7 +1846,7 @@ func_shell_base (char *o, char **argv, int trim_newlines) return o; /* Note the mktemp() is a security hole, but this only runs on Amiga. - Ideally we would use main.c:open_tmpfile(), but this uses a special + Ideally we would use output_tmpfile(), but this uses a special Open(), not fopen(), and I'm not familiar enough with the code to mess with it. */ strcpy (tmp_output, "t:MakeshXXXXXXXX"); diff --git a/job.c b/job.c index 28c8e375..6480d8de 100644 --- a/job.c +++ b/job.c @@ -1738,7 +1738,7 @@ new_job (struct file *file) 'struct child', and add that to the chain. */ c = xcalloc (sizeof (struct child)); - output_init (&c->output, output_sync); + output_init (&c->output); c->file = file; c->sh_batch_file = NULL; @@ -2588,7 +2588,7 @@ construct_command_argv_internal (char *line, char **restp, char *shell, "date", "del", "dir", "echo", "echo.", "endlocal", "erase", "exit", "for", "ftype", "goto", "if", "if", "md", "mkdir", "move", - "path", "pause", "prompt", "rd", "rem", "ren", + "path", "pause", "prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "setlocal", "shift", "time", "title", "type", "ver", "verify", "vol", ":", 0 }; diff --git a/main.c b/main.c index 47855148..4d6b5b6c 100644 --- a/main.c +++ b/main.c @@ -954,7 +954,7 @@ find_and_set_default_shell (const char *token) { *ep = '\0'; - sprintf (sh_path, "%s/%s", p, search_token); + sprintf (sh_path, "%s/%s", p, search_token); if (_access (sh_path, 0) == 0) { default_shell = xstrdup (w32ify (sh_path, 0)); @@ -974,15 +974,15 @@ find_and_set_default_shell (const char *token) } /* be sure to check last element of Path */ - if (p && *p) - { - sprintf (sh_path, "%s/%s", p, search_token); - if (_access (sh_path, 0) == 0) - { - default_shell = xstrdup (w32ify (sh_path, 0)); - sh_found = 1; - } - } + if (p && *p) + { + sprintf (sh_path, "%s/%s", p, search_token); + if (_access (sh_path, 0) == 0) + { + default_shell = xstrdup (w32ify (sh_path, 0)); + sh_found = 1; + } + } if (sh_found) DB (DB_VERBOSE, @@ -1154,24 +1154,7 @@ main (int argc, char **argv, char **envp) # endif #endif - /* Make sure stdout is line-buffered. */ - -#ifdef HAVE_SETVBUF -# ifdef SETVBUF_REVERSED - setvbuf (stdout, _IOLBF, xmalloc (BUFSIZ), BUFSIZ); -# else /* setvbuf not reversed. */ - /* Some buggy systems lose if we pass 0 instead of allocating ourselves. */ - setvbuf (stdout, 0, _IOLBF, BUFSIZ); -# endif /* setvbuf reversed. */ -#elif HAVE_SETLINEBUF - setlinebuf (stdout); -#endif /* setlinebuf missing. */ - - /* Configure stdout/stderr to be in append mode. - This keeps parallel jobs from losing output due to overlapping writes. */ - - set_append_mode (fileno (stdout)); - set_append_mode (fileno (stderr)); + output_init (NULL); /* Figure out where this program lives. */ @@ -1418,16 +1401,16 @@ main (int argc, char **argv, char **envp) /* Set always_make_flag if -B was given and we've not restarted already. */ always_make_flag = always_make_set && (restarts == 0); - /* Print version information. */ - if (print_version_flag || ISDB (DB_BASIC)) + /* Print version information, and exit. */ + if (print_version_flag) { print_version (); - - /* 'make --version' is supposed to just print the version and exit. */ - if (print_version_flag) - die (0); + die (0); } + if (ISDB (DB_BASIC)) + print_version (); + #ifndef VMS /* Set the "MAKE_COMMAND" variable to the name we were invoked with. (If it is a relative pathname with a slash, prepend our directory name @@ -1670,7 +1653,7 @@ main (int argc, char **argv, char **envp) #endif /* !HAVE_DOS_PATHS */ strcat (template, DEFAULT_TMPFILE); - outfile = open_tmpfile (&stdin_nm, template); + outfile = output_tmpfile (&stdin_nm, template); if (outfile == 0) pfatal_with_name (_("fopen (temporary file)")); while (!feof (stdin) && ! ferror (stdin)) @@ -1945,7 +1928,7 @@ main (int argc, char **argv, char **envp) #ifdef WINDOWS32 /* sub_proc.c cannot wait for more than MAXIMUM_WAIT_OBJECTS objects - * and one of them is the job-server semaphore object. Limit the + * and one of them is the job-server semaphore object. Limit the * number of available job slots to (MAXIMUM_WAIT_OBJECTS - 1). */ if (job_slots >= MAXIMUM_WAIT_OBJECTS) diff --git a/makeint.h b/makeint.h index 2674eaf0..276a13d9 100644 --- a/makeint.h +++ b/makeint.h @@ -449,9 +449,6 @@ int alpha_compare (const void *, const void *); void print_spaces (unsigned int); char *find_percent (char *); const char *find_percent_cached (const char **); -void set_append_mode (int); -int open_tmpfd (void); -FILE *open_tmpfile (char **, const char *); #ifndef NO_ARCHIVES int ar_name (const char *); diff --git a/misc.c b/misc.c index 48581178..df8bd43a 100644 --- a/misc.c +++ b/misc.c @@ -704,7 +704,7 @@ child_access (void) #endif /* GETLOADAVG_PRIVILEGED */ } - + #ifdef NEED_GET_PATH_MAX unsigned int get_path_max (void) @@ -725,100 +725,6 @@ get_path_max (void) #endif -/* Set a file descriptor to be in O_APPEND mode. - If it fails, just ignore it. */ - -void -set_append_mode (int fd) -{ -#if defined(F_GETFL) && defined(F_SETFL) && defined(O_APPEND) - int flags = fcntl (fd, F_GETFL, 0); - if (flags >= 0) - fcntl (fd, F_SETFL, flags | O_APPEND); -#endif -} - -/* Provide support for temporary files. */ - -#ifndef HAVE_STDLIB_H -# ifdef HAVE_MKSTEMP -int mkstemp (char *template); -# else -char *mktemp (char *template); -# endif -#endif - -/* This is only used by output-sync, and it may not be portable. */ -#ifdef OUTPUT_SYNC - -/* Returns a file descriptor to a temporary file. The file is automatically - closed/deleted on exit. Don't use a FILE* stream. */ -int -open_tmpfd () -{ - int fd = -1; - FILE *tfile = tmpfile (); - - if (! tfile) - pfatal_with_name ("tmpfile"); - - /* Create a duplicate so we can close the stream. */ - fd = dup (fileno (tfile)); - if (fd < 0) - pfatal_with_name ("dup"); - - fclose (tfile); - - set_append_mode (fd); - - return fd; -} - -#endif - -FILE * -open_tmpfile (char **name, const char *template) -{ -#ifdef HAVE_FDOPEN - int fd; -#endif - -#if defined HAVE_MKSTEMP || defined HAVE_MKTEMP -# define TEMPLATE_LEN strlen (template) -#else -# define TEMPLATE_LEN L_tmpnam -#endif - *name = xmalloc (TEMPLATE_LEN + 1); - strcpy (*name, template); - -#if defined HAVE_MKSTEMP && defined HAVE_FDOPEN - /* It's safest to use mkstemp(), if we can. */ - fd = mkstemp (*name); - if (fd == -1) - return 0; - return fdopen (fd, "w"); -#else -# ifdef HAVE_MKTEMP - (void) mktemp (*name); -# else - (void) tmpnam (*name); -# endif - -# ifdef HAVE_FDOPEN - /* Can't use mkstemp(), but guard against a race condition. */ - fd = open (*name, O_CREAT|O_EXCL|O_WRONLY, 0600); - if (fd == -1) - return 0; - return fdopen (fd, "w"); -# else - /* Not secure, but what can we do? */ - return fopen (*name, "w"); -# endif -#endif -} - - - /* This code is stolen from gnulib. If/when we abandon the requirement to work with K&R compilers, we can remove this (and perhaps other parts of GNU make!) and migrate to using diff --git a/output.c b/output.c index 6d621c2f..2e69c6d2 100644 --- a/output.c +++ b/output.c @@ -45,7 +45,7 @@ static unsigned int stdio_traced = 0; #define va_copy(_d, _s) ((_d) = (_s)) #define snprintf msc_vsnprintf static int -msc_vsnprintf(char *str, size_t size, const char *format, va_list ap) +msc_vsnprintf (char *str, size_t size, const char *format, va_list ap) { int len = -1; @@ -60,9 +60,9 @@ msc_vsnprintf(char *str, size_t size, const char *format, va_list ap) /* Write a string to the current STDOUT or STDERR. */ static void -_outputs (int is_err, const char *msg) +_outputs (struct output *out, int is_err, const char *msg) { - if (! output_context || ! output_context->syncout) + if (! out || ! out->syncout) { FILE *f = is_err ? stderr : stdout; fputs (msg, f); @@ -70,7 +70,7 @@ _outputs (int is_err, const char *msg) } else { - int fd = is_err ? output_context->err : output_context->out; + int fd = is_err ? out->err : out->out; int len = strlen (msg); int r; @@ -92,7 +92,7 @@ _outputs (int is_err, const char *msg) left (according to ENTERING) the current directory. */ static int -log_working_directory (int entering) +log_working_directory (struct output *out, int entering) { static char *buf = NULL; static unsigned int len = 0; @@ -100,10 +100,6 @@ log_working_directory (int entering) const char *fmt; char *p; - /* Only print if directory logging is enabled. */ - if (entering && ! print_directory_flag) - return 0; - /* Get enough space for the longest possible output. */ need = strlen (program) + INTEGER_LENGTH + 2 + 1; if (starting_directory) @@ -158,10 +154,23 @@ log_working_directory (int entering) else sprintf (p, fmt, program, makelevel, starting_directory); - _outputs (0, buf); + _outputs (out, 0, buf); return 1; } + +/* Set a file descriptor to be in O_APPEND mode. + If it fails, just ignore it. */ + +static void +set_append_mode (int fd) +{ +#if defined(F_GETFL) && defined(F_SETFL) && defined(O_APPEND) + int flags = fcntl (fd, F_GETFL, 0); + if (flags >= 0) + fcntl (fd, F_SETFL, flags | O_APPEND); +#endif +} #ifdef OUTPUT_SYNC @@ -277,6 +286,73 @@ release_semaphore (void *sem) perror ("fcntl()"); } +/* Returns a file descriptor to a temporary file. The file is automatically + closed/deleted on exit. Don't use a FILE* stream. */ +int +output_tmpfd () +{ + int fd = -1; + FILE *tfile = tmpfile (); + + if (! tfile) + pfatal_with_name ("tmpfile"); + + /* Create a duplicate so we can close the stream. */ + fd = dup (fileno (tfile)); + if (fd < 0) + pfatal_with_name ("dup"); + + fclose (tfile); + + set_append_mode (fd); + + return fd; +} + +/* Adds file descriptors to the child structure to support output_sync; one + for stdout and one for stderr as long as they are open. If stdout and + stderr share a device they can share a temp file too. + Will reset output_sync on error. */ +static void +setup_tmpfile (struct output *out) +{ + /* Is make's stdout going to the same place as stderr? */ + static int combined_output = -1; + + if (combined_output < 0) + combined_output = sync_init (); + + if (STREAM_OK (stdout)) + { + int fd = output_tmpfd (); + if (fd < 0) + goto error; + CLOSE_ON_EXEC (fd); + out->out = fd; + } + + if (STREAM_OK (stderr)) + { + if (out->out != OUTPUT_NONE && combined_output) + out->err = out->out; + else + { + int fd = output_tmpfd (); + if (fd < 0) + goto error; + CLOSE_ON_EXEC (fd); + out->err = fd; + } + } + + return; + + /* If we failed to create a temp file, disable output sync going forward. */ + error: + output_close (out); + output_sync = 0; +} + /* Synchronize the output of jobs in -j mode to keep the results of each job together. This is done by holding the results in temp files, one for stdout and potentially another for stderr, and only releasing @@ -290,15 +366,15 @@ output_dump (struct output *out) if (outfd_not_empty || errfd_not_empty) { - int logged = 0; + int traced = 0; /* Try to acquire the semaphore. If it fails, dump the output unsynchronized; still better than silently discarding it. */ void *sem = acquire_semaphore (); - /* Log the working directory, if we need to. */ - if (out->syncout) - logged = log_working_directory (1); + /* Log the working directory for this dump. */ + if (print_directory_flag && output_sync != OUTPUT_SYNC_RECURSE) + traced = log_working_directory (output_context, 1); /* We've entered the "critical section" during which a lock is held. We want to keep it as short as possible. */ @@ -307,8 +383,8 @@ output_dump (struct output *out) if (errfd_not_empty && out->err != out->out) pump_from_tmp (out->err, stderr); - if (logged) - log_working_directory (0); + if (traced) + log_working_directory (output_context, 0); /* Exit the critical section. */ if (sem) @@ -329,58 +405,88 @@ output_dump (struct output *out) } } } - -/* Adds file descriptors to the child structure to support output_sync; one - for stdout and one for stderr as long as they are open. If stdout and - stderr share a device they can share a temp file too. - Will reset output_sync on error. */ -static void -setup_tmpfile (struct output *out) -{ - /* Is make's stdout going to the same place as stderr? */ - static int combined_output = -1; - - if (combined_output < 0) - combined_output = sync_init (); - - if (STREAM_OK (stdout)) - { - int fd = open_tmpfd (); - if (fd < 0) - goto error; - CLOSE_ON_EXEC (fd); - out->out = fd; - } - - if (STREAM_OK (stderr)) - { - if (out->out != OUTPUT_NONE && combined_output) - out->err = out->out; - else - { - int fd = open_tmpfd (); - if (fd < 0) - goto error; - CLOSE_ON_EXEC (fd); - out->err = fd; - } - } - - return; - - /* If we failed to create a temp file, disable output sync going forward. */ - error: - output_close (out); - output_sync = 0; -} #endif /* OUTPUT_SYNC */ -void -output_init (struct output *out, unsigned int syncout) +/* Provide support for temporary files. */ + +#ifndef HAVE_STDLIB_H +# ifdef HAVE_MKSTEMP +int mkstemp (char *template); +# else +char *mktemp (char *template); +# endif +#endif + +FILE * +output_tmpfile (char **name, const char *template) { - out->out = out->err = OUTPUT_NONE; - out->syncout = !!syncout; +#ifdef HAVE_FDOPEN + int fd; +#endif + +#if defined HAVE_MKSTEMP || defined HAVE_MKTEMP +# define TEMPLATE_LEN strlen (template) +#else +# define TEMPLATE_LEN L_tmpnam +#endif + *name = xmalloc (TEMPLATE_LEN + 1); + strcpy (*name, template); + +#if defined HAVE_MKSTEMP && defined HAVE_FDOPEN + /* It's safest to use mkstemp(), if we can. */ + fd = mkstemp (*name); + if (fd == -1) + return 0; + return fdopen (fd, "w"); +#else +# ifdef HAVE_MKTEMP + (void) mktemp (*name); +# else + (void) tmpnam (*name); +# endif + +# ifdef HAVE_FDOPEN + /* Can't use mkstemp(), but guard against a race condition. */ + fd = open (*name, O_CREAT|O_EXCL|O_WRONLY, 0600); + if (fd == -1) + return 0; + return fdopen (fd, "w"); +# else + /* Not secure, but what can we do? */ + return fopen (*name, "w"); +# endif +#endif +} + + +void +output_init (struct output *out) +{ + if (out) + { + out->out = out->err = OUTPUT_NONE; + out->syncout = !!output_sync; + return; + } + + /* Configure this instance of make. Be sure stdout is line-buffered. */ + +#ifdef HAVE_SETVBUF +# ifdef SETVBUF_REVERSED + setvbuf (stdout, _IOLBF, xmalloc (BUFSIZ), BUFSIZ); +# else /* setvbuf not reversed. */ + /* Some buggy systems lose if we pass 0 instead of allocating ourselves. */ + setvbuf (stdout, 0, _IOLBF, BUFSIZ); +# endif /* setvbuf reversed. */ +#elif HAVE_SETLINEBUF + setlinebuf (stdout); +#endif /* setlinebuf missing. */ + + /* Force stdout/stderr into append mode. This ensures parallel jobs won't + lose output due to overlapping writes. */ + set_append_mode (fileno (stdout)); + set_append_mode (fileno (stderr)); } void @@ -389,7 +495,7 @@ output_close (struct output *out) if (! out) { if (stdio_traced) - log_working_directory (0); + log_working_directory (NULL, 0); return; } @@ -402,46 +508,34 @@ output_close (struct output *out) if (out->err >= 0 && out->err != out->out) close (out->err); - output_init (out, 0); + output_init (out); } -/* We're about to run a sub-process so ensure we've got our output set up. */ +/* We're about to generate output: be sure it's set up. */ void output_start () { - if (! output_context) - { - if (! stdio_traced) - stdio_traced = log_working_directory (1); - } #ifdef OUTPUT_SYNC - else if (output_context->syncout && ! OUTPUT_ISSET(output_context)) + if (output_context && output_context->syncout && ! OUTPUT_ISSET(output_context)) setup_tmpfile (output_context); #endif + + if (! output_context || output_sync == OUTPUT_SYNC_RECURSE) + { + if (! stdio_traced && print_directory_flag) + stdio_traced = log_working_directory (NULL, 1); + } } void outputs (int is_err, const char *msg) { - /* For stdio, an empty msg means we're about to invoke a shell command, - which may or may not generate output, so log the directory. */ - if (! output_context && ! stdio_traced) - stdio_traced = log_working_directory (1); - - /* Don't bother to do anything with empty strings. */ if (! msg || *msg == '\0') return; -#ifdef OUTPUT_SYNC - if (output_context) - { - /* Create a temporary file to write to, if necessary. */ - if (output_context->syncout && ! OUTPUT_ISSET(output_context)) - setup_tmpfile (output_context); - } -#endif + output_start (); - _outputs (is_err, msg); + _outputs (output_context, is_err, msg); } @@ -465,7 +559,7 @@ vfmtconcat (const char *fmt, va_list args) int tot; int unused = fmtbuf.size - fmtbuf.len; - va_copy(vcopy, args); + va_copy (vcopy, args); tot = vsnprintf (&fmtbuf.buffer[fmtbuf.len], unused, fmt, args); assert (tot >= 0); @@ -479,7 +573,7 @@ vfmtconcat (const char *fmt, va_list args) tot = vsnprintf (&fmtbuf.buffer[fmtbuf.len], unused, fmt, vcopy); } - va_end(vcopy); + va_end (vcopy); fmtbuf.len += tot; diff --git a/output.h b/output.h index a9185046..2bb1c391 100644 --- a/output.h +++ b/output.h @@ -26,12 +26,22 @@ extern struct output *output_context; #define OUTPUT_SET(_new) do{ if ((_new)->syncout) output_context = (_new); }while(0) #define OUTPUT_UNSET() do{ output_context = NULL; }while(0) -void output_init (struct output *out, unsigned int syncout); +FILE *output_tmpfile (char **, const char *); + +/* Initialize and close a child output structure: if NULL do this program's + output (this should only be done once). */ +void output_init (struct output *out); void output_close (struct output *out); +/* In situations where output may be about to be displayed but we're not + sure if we've set it up yet, call this. */ void output_start (void); + +/* Show a message on stdout or stderr. Will start the output if needed. */ void outputs (int is_err, const char *msg); #ifdef OUTPUT_SYNC +int output_tmpfd (void); +/* Dump any child output content to stdout, and reset it. */ void output_dump (struct output *out); #endif diff --git a/tests/ChangeLog b/tests/ChangeLog index 5fa17986..5eff7ec0 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,7 @@ +2013-09-14 Paul Smith + + * scripts/features/output-sync: Verify -Orecurse properly. + 2013-09-12 Paul Smith * scripts/features/output-sync: Modify for output sync behavior. diff --git a/tests/scripts/features/output-sync b/tests/scripts/features/output-sync index 55af0a1b..a6b24563 100644 --- a/tests/scripts/features/output-sync +++ b/tests/scripts/features/output-sync @@ -135,8 +135,6 @@ foo: end #MAKE#[1]: Entering directory '#PWD#/bar' bar: start bar: end -#MAKE#[1]: Leaving directory '#PWD#/bar' -#MAKE#[1]: Entering directory '#PWD#/bar' baz: start baz: end #MAKE#[1]: Leaving directory '#PWD#/bar'\n", 0, 6); diff --git a/vmsjobs.c b/vmsjobs.c index 8d30157d..8bacc866 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); + /* 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; + 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; + 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; - } + 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; + $DESCRIPTOR(inputDsc,"SYS$COMMAND"); + int status; + struct { + short int status, count; + int dvi; + } iosb; + unsigned short int loc_chan; - setupYAstTried++; + 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; - } + 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; + /* 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; + && (*(p+1) == 'm') + && ((*(p+2) == ' ') || (*(p+2) == '\t'))) + { + int in_arg; - /* rm */ - p += 3; - while ((*p == ' ') || (*p == '\t')) - p++; - in_arg = 1; + /* 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++; - } - } + 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))