From af834e6cd7732605685c251d46a9fd49005e76c3 Mon Sep 17 00:00:00 2001 From: jay Date: Sat, 15 Jan 2005 09:32:49 +0000 Subject: [PATCH] First working version of -exec ...+ --- find/defs.h | 16 +++++-- find/find.c | 83 +++++++++++++++++++++++++++++++------ find/fstype.c | 1 + find/parser.c | 54 ++++++++++++++---------- find/pred.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- lib/buildcmd.c | 19 +++++++-- lib/buildcmd.h | 15 +++++-- xargs/xargs.c | 10 ++--- 8 files changed, 265 insertions(+), 62 deletions(-) diff --git a/find/defs.h b/find/defs.h index 3997edb..ae77ba7 100644 --- a/find/defs.h +++ b/find/defs.h @@ -208,8 +208,8 @@ struct size_val }; #define NEW_EXEC 1 -#undef NEW_EXEC /* +#undef NEW_EXEC */ #if !defined(NEW_EXEC) @@ -221,13 +221,15 @@ struct path_arg }; #endif +#include "buildcmd.h" + struct exec_val { #if defined(NEW_EXEC) /* new-style */ boolean multiple; /* -exec {} \+ denotes multiple argument. */ - struct buildcmd_control *ctl; - struct buildcmd_state *state; + struct buildcmd_control ctl; + struct buildcmd_state state; char **replace_vec; /* Command arguments (for ";" style) */ int num_args; #else @@ -430,7 +432,15 @@ boolean pred_uid PARAMS((char *pathname, struct stat *stat_buf, struct predicate boolean pred_used PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_user PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_xtype PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); + +int launch PARAMS((const struct buildcmd_control *ctl, + struct buildcmd_state *buildstate)); + + char *find_pred_name PARAMS((PFB pred_func)); + + + #ifdef DEBUG void print_tree PARAMS((FILE*, struct predicate *node, int indent)); void print_list PARAMS((FILE*, struct predicate *node)); diff --git a/find/find.c b/find/find.c index 4ef964e..25a01d0 100644 --- a/find/find.c +++ b/find/find.c @@ -23,6 +23,7 @@ The idea for -print0 and xargs -0 came from Dan Bernstein . */ + #include "defs.h" #define USE_SAFE_CHDIR 1 @@ -36,12 +37,14 @@ #else #include #endif + #include "../gnulib/lib/xalloc.h" #include "../gnulib/lib/human.h" #include "../gnulib/lib/canonicalize.h" #include "closeout.h" #include #include "../gnulib/lib/savedir.h" +#include "buildcmd.h" #ifdef HAVE_LOCALE_H #include @@ -71,6 +74,9 @@ static void process_top_path PARAMS((char *pathname)); static int process_path PARAMS((char *pathname, char *name, boolean leaf, char *parent)); static void process_dir PARAMS((char *pathname, char *name, int pathlen, struct stat *statp, char *parent)); +static void complete_pending_execdirs(struct predicate *p); +static void complete_pending_execs (struct predicate *p); + static boolean default_prints PARAMS((struct predicate *pred)); @@ -493,6 +499,12 @@ main (int argc, char **argv) process_top_path (defaultpath); } + /* If "-exec ... {} +" has been used, there may be some + * partially-full command lines which have been built, + * but which are not yet complete. Execute those now. + */ + complete_pending_execs(predicates); + return state.exit_status; } @@ -1121,25 +1133,72 @@ process_path (char *pathname, char *name, boolean leaf, char *parent) * have no effect if there are no arguments waiting). */ static void -complete_pending_execdirs(void) +complete_pending_execdirs(struct predicate *p) { #if defined(NEW_EXEC) - struct predicate *p; - for (p=eval_tree; p; p=p->pred_next) + if (NULL == p) + return; + + complete_pending_execdirs(p->pred_left); + + if (p->pred_func == pred_execdir || p->pred_func == pred_okdir) { - if (p->pred_func == pred_execdir - || p->pred_func == pred_okdir) + /* It's an exec-family predicate. p->args.exec_val is valid. */ + if (p->args.exec_vec.multiple) { - /* It's an exec-family predicate. p->args.exec_val is valid. */ - if (p->args.exec_vec.multiple) + struct exec_val *execp = &p->args.exec_vec; + + /* This one was terminated by '+' and so might have some + * left... Run it if neccessary. + */ + if (execp->state.todo) { - /* This one was terminated by '+' - * and so might have some left... Run it. - * Set state.exit_status if there are any problems. - */ + /* There are not-yet-executed arguments. */ + launch (&execp->ctl, &execp->state); } } } + + complete_pending_execdirs(p->pred_right); +#else + /* nothing to do. */ + return; +#endif +} + +/* Examine the predicate list for instances of -exec which have been + * terminated with '+' (build argument list) rather than ';' (singles + * only). If there are any, run them (this will have no effect if + * there are no arguments waiting). + */ +static void +complete_pending_execs(struct predicate *p) +{ +#if defined(NEW_EXEC) + if (NULL == p) + return; + + complete_pending_execs(p->pred_left); + + /* It's an exec-family predicate then p->args.exec_val is valid + * and we can check it. + */ + if (p->pred_func == pred_exec && p->args.exec_vec.multiple) + { + struct exec_val *execp = &p->args.exec_vec; + + /* This one was terminated by '+' and so might have some + * left... Run it if neccessary. Set state.exit_status if + * there are any problems. + */ + if (execp->state.todo) + { + /* There are not-yet-executed arguments. */ + launch (&execp->ctl, &execp->state); + } + } + + complete_pending_execs(p->pred_right); #else /* nothing to do. */ return; @@ -1294,7 +1353,7 @@ process_dir (char *pathname, char *name, int pathlen, struct stat *statp, char * * which have been built but have not yet been processed, do them now * because they must be done in the same directory. */ - complete_pending_execdirs(); + complete_pending_execdirs(predicates); #if USE_SAFE_CHDIR if (strcmp (name, ".")) diff --git a/find/fstype.c b/find/fstype.c index 6c45209..2b98666 100644 --- a/find/fstype.c +++ b/find/fstype.c @@ -24,6 +24,7 @@ * of manual hacking of configure.in). */ + #include #include #include diff --git a/find/parser.c b/find/parser.c index d286d32..fbb1313 100644 --- a/find/parser.c +++ b/find/parser.c @@ -17,6 +17,7 @@ USA. */ + #include "defs.h" #include #include @@ -1850,6 +1851,11 @@ new_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr) if ((argv == NULL) || (argv[*arg_ptr] == NULL)) return (false); + our_pred = insert_primary (func); + our_pred->side_effects = true; + our_pred->no_default_print = true; + execp = &our_pred->args.exec_vec; + if ((func != pred_okdir) && (func != pred_ok)) allow_plus = true; else @@ -1901,6 +1907,7 @@ new_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr) if ((end == start) || (argv[end] == NULL)) { *arg_ptr = end; + free(our_pred); return false; } @@ -1918,13 +1925,8 @@ new_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr) suffix); } - our_pred = insert_primary (func); - our_pred->side_effects = true; - our_pred->no_default_print = true; - execp = &our_pred->args.exec_vec; - - execp->ctl = xmalloc(sizeof(*(execp->ctl))); - bc_init_controlinfo(execp->ctl); + /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */ + bc_init_controlinfo(&execp->ctl); if (our_pred->args.exec_vec.multiple) { @@ -1932,19 +1934,22 @@ new_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr) * command and initial arguments. */ execp->replace_vec = NULL; - execp->ctl->replace_pat = NULL; - execp->ctl->rplen = 0; - execp->ctl->lines_per_exec = 0; /* no limit */ - execp->ctl->args_per_exec = 0; /* no limit */ + execp->ctl.replace_pat = NULL; + execp->ctl.rplen = 0; + execp->ctl.lines_per_exec = 0; /* no limit */ + execp->ctl.args_per_exec = 0; /* no limit */ /* remember how many arguments there are */ - execp->ctl->initial_argc = end; + execp->ctl.initial_argc = (end-start) - 1; - /* Gather the initial arguments. */ - for (i=start; istate = xmalloc(sizeof struct buildcmd_state); */ + bc_init_state(&execp->ctl, &execp->state); + + /* Gather the initial arguments. Skip the {}. */ + for (i=start; ictl, execp->state, - argv[i], strlen(argv[i]), 1); + bc_push_arg(&execp->ctl, &execp->state, + argv[i], strlen(argv[i])+1, 1); } } else @@ -1954,11 +1959,16 @@ new_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr) */ execp->num_args = end - start; - execp->ctl->replace_pat = "{}"; - execp->ctl->rplen = strlen(execp->ctl->replace_pat); - execp->ctl->lines_per_exec = 0; /* no limit */ - execp->ctl->args_per_exec = 1; + execp->ctl.replace_pat = "{}"; + execp->ctl.rplen = strlen(execp->ctl.replace_pat); + execp->ctl.lines_per_exec = 0; /* no limit */ + execp->ctl.args_per_exec = 0; /* no limit */ execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args); + + + /* execp->state = xmalloc(sizeof(*(execp->state))); */ + bc_init_state(&execp->ctl, &execp->state); + /* Remember the (pre-replacement) arguments for later. */ for (i=0; inum_args; ++i) { @@ -1966,9 +1976,6 @@ new_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr) } } - execp->state = xmalloc(sizeof(*(execp->state))); - bc_init_state(execp->ctl, execp->state); - if (argv[end] == NULL) *arg_ptr = end; else @@ -2010,6 +2017,7 @@ old_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr) our_pred->side_effects = true; our_pred->no_default_print = true; execp = &our_pred->args.exec_vec; + execp->usercontext = our_pred; execp->paths = (struct path_arg *) xmalloc (sizeof (struct path_arg) * (num_paths + 1)); execp->vec = (char **) xmalloc (sizeof (char *) * (end - start + 1)); diff --git a/find/pred.c b/find/pred.c index 8ba28a7..4f00103 100644 --- a/find/pred.c +++ b/find/pred.c @@ -23,6 +23,9 @@ #include #include #include +#include +#include +#include #include "../gnulib/lib/xalloc.h" #include "../gnulib/lib/dirname.h" #include "../gnulib/lib/human.h" @@ -140,7 +143,7 @@ extern int yesno (); #define MAX(a, b) ((a) > (b) ? (a) : (b)) static boolean insert_lname PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case)); -static boolean launch PARAMS((struct predicate *pred_ptr)); + static char *format_date PARAMS((time_t when, int kind)); static char *ctime_format PARAMS((time_t when)); @@ -459,32 +462,39 @@ new_impl_pred_exec (char *pathname, struct stat *stat_buf, struct predicate *pre { struct exec_val *execp = &pred_ptr->args.exec_vec; + execp->ctl.exec_callback = launch; + if (execp->multiple) { /* Push the argument onto the current list. * The command may or may not be run at this point, * depending on the command line length limits. */ - bc_push_arg(execp->ctl, - execp->state, - pathname, strlen(pathname), 0); + bc_push_arg(&execp->ctl, + &execp->state, + pathname, strlen(pathname)+1, 0); + + /* POSIX: If the primary expression is punctuated by a plus + * sign, the primary shall always evaluate as true + */ + return 0; } else { - int i; + int i, retval; for (i=0; inum_args; ++i) { - bc_do_insert(execp->ctl, - execp->state, + bc_do_insert(&execp->ctl, + &execp->state, execp->replace_vec[i], strlen(execp->replace_vec[i]), pathname, strlen(pathname), 0); } /* Actually invoke the command. */ - execp->ctl->exec_callback( - execp->ctl, - execp->state); + retval = execp->ctl.exec_callback(&execp->ctl, + &execp->state); + return retval; } } #else @@ -1455,7 +1465,6 @@ pred_xtype (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) return (pred_type (pathname, &sbuf, pred_ptr)); } -#if !defined(NEW_EXEC) /* 1) fork to get a child; parent remembers the child pid 2) child execs the command requested 3) parent waits for child; checks for proper pid of child @@ -1474,6 +1483,104 @@ pred_xtype (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) zero, and the exit arg (status high) is 0. Otherwise return false, possibly printing an error message. */ +#if defined(NEW_EXEC) +static void +prep_child_for_exec (void) +{ + const char inputfile[] = "/dev/null"; + /* fprintf(stderr, "attaching stdin to /dev/null\n"); */ + + close(0); + if (open(inputfile, O_RDONLY) < 0) + { + /* This is not entirely fatal, since + * executing the child with a closed + * stdin is almost as good as executing it + * with its stdin attached to /dev/null. + */ + error (0, errno, "%s", inputfile); + } +} + + + +int +launch (const struct buildcmd_control *ctl, + struct buildcmd_state *buildstate) +{ + int wait_status; + pid_t child_pid; + static int first_time = 1; + + /* Null terminate the arg list. */ + bc_push_arg (ctl, buildstate, (char *) NULL, 0, false); + + /* Make sure output of command doesn't get mixed with find output. */ + fflush (stdout); + fflush (stderr); + + /* Make sure to listen for the kids. */ + if (first_time) + { + first_time = 0; + signal (SIGCHLD, SIG_DFL); + } + + child_pid = fork (); + if (child_pid == -1) + error (1, errno, _("cannot fork")); + if (child_pid == 0) + { + /* We be the child. */ + prep_child_for_exec(); + if (starting_desc < 0 + ? chdir (starting_dir) != 0 + : fchdir (starting_desc) != 0) + { + error (0, errno, "%s", starting_dir); + _exit (1); + } + execvp (buildstate->cmd_argv[0], buildstate->cmd_argv); + error (0, errno, "%s", buildstate->cmd_argv[0]); + _exit (1); + } + + + /* In parent; set up for next time. */ + bc_clear_args(ctl, buildstate); + + + while (waitpid (child_pid, &wait_status, 0) == (pid_t) -1) + { + if (errno != EINTR) + { + error (0, errno, _("error waiting for %s"), buildstate->cmd_argv[0]); + state.exit_status = 1; + return 0; /* FAIL */ + } + } + + if (WIFSIGNALED (wait_status)) + { + error (0, 0, _("%s terminated by signal %d"), + buildstate->cmd_argv[0], WTERMSIG (wait_status)); + state.exit_status = 1; + return 1; /* OK */ + } + + if (0 == WEXITSTATUS (wait_status)) + { + return 1; /* OK */ + } + else + { + + state.exit_status = 1; + return 0; /* FAIL */ + } + +} +#else static boolean launch (struct predicate *pred_ptr) { diff --git a/lib/buildcmd.c b/lib/buildcmd.c index a40961f..c71bcb0 100644 --- a/lib/buildcmd.c +++ b/lib/buildcmd.c @@ -175,6 +175,7 @@ bc_push_arg (const struct buildcmd_control *ctl, size_t len, int initial_args) { + state->todo = 1; if (arg) { if (state->cmd_argv_chars + len > ctl->arg_max) @@ -226,6 +227,12 @@ bc_push_arg (const struct buildcmd_control *ctl, && (state->cmd_argc - ctl->initial_argc) == ctl->args_per_exec) do_exec (ctl, state); } + + /* If this is an initial argument, set the high-water mark. */ + if (initial_args) + { + state->cmd_initial_argv_chars = state->cmd_argv_chars; + } } @@ -294,12 +301,14 @@ bc_get_arg_max(void) } -static void cb_exec_noop(const struct buildcmd_control *ctl, +static int cb_exec_noop(const struct buildcmd_control *ctl, struct buildcmd_state *state) { /* does nothing. */ (void) ctl; (void) state; + + return 0; } void @@ -325,14 +334,16 @@ bc_init_state(const struct buildcmd_control *ctl, state->cmd_argv_alloc = 0; state->argbuf = (char *) xmalloc (ctl->arg_max + 1); state->cmd_argv_chars = 0; + state->todo = 0; + state->usercontext = NULL; } void bc_clear_args(const struct buildcmd_control *ctl, - struct buildcmd_state *state, - int initial_argv_chars) + struct buildcmd_state *state) { state->cmd_argc = ctl->initial_argc; - state->cmd_argv_chars = initial_argv_chars; + state->cmd_argv_chars = state->cmd_initial_argv_chars; + state->todo = 0; } diff --git a/lib/buildcmd.h b/lib/buildcmd.h index 7f77bee..d2a12b4 100644 --- a/lib/buildcmd.h +++ b/lib/buildcmd.h @@ -39,6 +39,14 @@ struct buildcmd_state /* Number of chars being used in `cmd_argv'. */ int cmd_argv_chars; + /* Number of chars being used in `cmd_argv' for the initial args.. */ + int cmd_initial_argv_chars; + + /* User context information. */ + void *usercontext; + + /* to-do flag. */ + int todo; }; @@ -63,7 +71,7 @@ struct buildcmd_control int initial_argc; /* 0 */ /* exec callback. */ - void (*exec_callback)(const struct buildcmd_control *, struct buildcmd_state *); + int (*exec_callback)(const struct buildcmd_control *, struct buildcmd_state *); /* If nonzero, the maximum number of nonblank lines from stdin to use per command line. */ @@ -87,12 +95,11 @@ extern void bc_push_arg (const struct buildcmd_control *ctl, char *arg, size_t len, int initial_args); extern void bc_init_state(const struct buildcmd_control *ctl, - struct buildcmd_state *state); + struct buildcmd_state *state); extern void bc_init_controlinfo(struct buildcmd_control *ctl); extern long bc_get_arg_max(void); extern void bc_clear_args(const struct buildcmd_control *ctl, - struct buildcmd_state *state, - int initial_argv_chars); + struct buildcmd_state *state); #endif diff --git a/xargs/xargs.c b/xargs/xargs.c index 6be6bb4..b132d1c 100644 --- a/xargs/xargs.c +++ b/xargs/xargs.c @@ -241,7 +241,7 @@ static int initial_argc = 0; #endif /* Number of chars in the initial args. */ -static int initial_argv_chars = 0; +/* static int initial_argv_chars = 0; */ /* true when building up initial arguments in `cmd_argv'. */ static boolean initial_args = true; @@ -578,7 +578,7 @@ main (int argc, char **argv) argv[optind], strlen (argv[optind]) + 1, initial_args); initial_args = false; bc_ctl.initial_argc = bc_state.cmd_argc; - initial_argv_chars = bc_state.cmd_argv_chars; + bc_state.cmd_initial_argv_chars = bc_state.cmd_argv_chars; while ((*read_args) () != -1) if (bc_ctl.lines_per_exec && lineno >= bc_ctl.lines_per_exec) @@ -693,7 +693,7 @@ read_line (void) int len; char *p = linebuf; /* Including the NUL, the args must not grow past this point. */ - char *endbuf = linebuf + bc_ctl.arg_max - initial_argv_chars - 1; + char *endbuf = linebuf + bc_ctl.arg_max - bc_state.cmd_initial_argv_chars - 1; if (eof) return -1; @@ -813,7 +813,7 @@ read_string (void) int len; char *p = linebuf; /* Including the NUL, the args must not grow past this point. */ - char *endbuf = linebuf + bc_ctl.arg_max - initial_argv_chars - 1; + char *endbuf = linebuf + bc_ctl.arg_max - bc_state.cmd_initial_argv_chars - 1; if (eof) return -1; @@ -943,7 +943,7 @@ xargs_do_exec (const struct buildcmd_control *ctl, struct buildcmd_state *state) add_proc (child); } - bc_clear_args(&bc_ctl, &bc_state, initial_argv_chars); + bc_clear_args(&bc_ctl, &bc_state); } /* Add the process with id PID to the list of processes that have -- 2.11.4.GIT