home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 8
/
FreshFishVol8-CD2.bin
/
bbs
/
gnu
/
findutils-4.1-src.lha
/
findutils-4.1
/
xargs
/
xargs.c
< prev
Wrap
C/C++ Source or Header
|
1994-11-12
|
22KB
|
919 lines
/* xargs -- build and execute command lines from standard input
Copyright (C) 1990, 91, 92, 93, 94 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Written by Mike Rendell <michael@cs.mun.ca>
and David MacKenzie <djm@gnu.ai.mit.edu>. */
#include <config.h>
#if __STDC__
#define P_(s) s
#else
#define P_(s) ()
#endif
#define _GNU_SOURCE
#include <ctype.h>
#if !defined (isascii) || defined (STDC_HEADERS)
#ifdef isascii
#undef isascii
#endif
#define isascii(c) 1
#endif
#ifdef isblank
#define ISBLANK(c) (isascii (c) && isblank (c))
#else
#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
#endif
#define ISSPACE(c) (ISBLANK (c) || (c) == '\n' || (c) == '\r' \
|| (c) == '\f' || (c) == '\v')
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <getopt.h>
#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
#include <string.h>
#if !defined(STDC_HEADERS)
#include <memory.h>
#endif
#else
#include <strings.h>
#define memcpy(dest, source, count) (bcopy((source), (dest), (count)))
#endif
char *strstr ();
char *strdup ();
#ifndef _POSIX_SOURCE
#include <sys/param.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <signal.h>
#if !defined(SIGCHLD) && defined(SIGCLD)
#define SIGCHLD SIGCLD
#endif
/* COMPAT: SYSV version defaults size (and has a max value of) to 470.
We try to make it as large as possible. */
#if !defined(ARG_MAX) && defined(_SC_ARG_MAX)
#define ARG_MAX sysconf (_SC_ARG_MAX)
#endif
#ifndef ARG_MAX
#define ARG_MAX NCARGS
#endif
#include "wait.h"
/* States for read_line. */
#define NORM 0
#define SPACE 1
#define QUOTE 2
#define BACKSLASH 3
#ifdef STDC_HEADERS
#include <stdlib.h>
#else
char *malloc ();
void exit ();
void free ();
long strtol ();
extern int errno;
#endif
/* Return nonzero if S is the EOF string. */
#define EOF_STR(s) (eof_str && *eof_str == *s && !strcmp (eof_str, s))
extern char **environ;
/* Not char because of type promotion; NeXT gcc can't handle it. */
typedef int boolean;
#define true 1
#define false 0
#if __STDC__
#define VOID void
#else
#define VOID char
#endif
VOID *xmalloc P_ ((size_t n));
VOID *xrealloc P_ ((VOID * p, size_t n));
void error P_ ((int status, int errnum, char *message,...));
extern char *version_string;
/* The name this program was run with. */
char *program_name;
/* Buffer for reading arguments from stdin. */
static char *linebuf;
/* Line number in stdin since the last command was executed. */
static int lineno = 0;
/* If nonzero, then instead of putting the args from stdin at
the end of the command argument list, they are each stuck into the
initial args, replacing each occurrence of the `replace_pat' in the
initial args. */
static char *replace_pat = NULL;
/* The length of `replace_pat'. */
static size_t rplen = 0;
/* If nonzero, when this string is read on stdin it is treated as
end of file.
I don't like this - it should default to NULL. */
static char *eof_str = "_";
/* If nonzero, the maximum number of nonblank lines from stdin to use
per command line. */
static long lines_per_exec = 0;
/* The maximum number of arguments to use per command line. */
static long args_per_exec = 1024;
/* If true, exit if lines_per_exec or args_per_exec is exceeded. */
static boolean exit_if_size_exceeded = false;
/* The maximum number of characters that can be used per command line. */
static long arg_max;
/* Storage for elements of `cmd_argv'. */
static char *argbuf;
/* The list of args being built. */
static char **cmd_argv = NULL;
/* Number of elements allocated for `cmd_argv'. */
static int cmd_argv_alloc = 0;
/* Number of valid elements in `cmd_argv'. */
static int cmd_argc = 0;
/* Number of chars being used in `cmd_argv'. */
static int cmd_argv_chars = 0;
/* Number of initial arguments given on the command line. */
static int initial_argc = 0;
/* Number of chars in the initial args. */
static int initial_argv_chars = 0;
/* true when building up initial arguments in `cmd_argv'. */
static boolean initial_args = true;
/* If nonzero, the maximum number of child processes that can be running
at once. */
static int proc_max = 1;
/* Total number of child processes that have been executed. */
static int procs_executed = 0;
/* The number of elements in `pids'. */
static int procs_executing = 0;
/* List of child processes currently executing. */
static pid_t *pids = NULL;
/* The number of allocated elements in `pids'. */
static int pids_alloc = 0;
/* Exit status; nonzero if any child process exited with a
status of 1-125. */
static int child_error = 0;
/* If true, print each command on stderr before executing it. */
static boolean print_command = false;
/* If true, query the user before executing each command, and only
execute the command if the user responds affirmatively. */
static boolean query_before_executing = false;
static struct option const longopts[] =
{
{"null", no_argument, NULL, '0'},
{"eof", optional_argument, NULL, 'e'},
{"replace", optional_argument, NULL, 'i'},
{"max-lines", optional_argument, NULL, 'l'},
{"max-args", required_argument, NULL, 'n'},
{"interactive", no_argument, NULL, 'p'},
{"no-run-if-empty", no_argument, NULL, 'r'},
{"max-chars", required_argument, NULL, 's'},
{"verbose", no_argument, NULL, 't'},
{"exit", no_argument, NULL, 'x'},
{"max-procs", required_argument, NULL, 'P'},
{"version", no_argument, NULL, 'v'},
{"help", no_argument, NULL, 'h'},
{NULL, no_argument, NULL, 0}
};
static int read_line P_ ((void));
static int read_string P_ ((void));
static void do_insert P_ ((char *arg, size_t arglen, size_t lblen));
static void push_arg P_ ((char *arg, size_t len));
static boolean print_args P_ ((boolean ask));
static void do_exec P_ ((void));
static void add_proc P_ ((pid_t pid));
static void wait_for_proc P_ ((boolean all));
static long parse_num P_ ((char *str, int option, long min, long max));
static long env_size P_ ((char **envp));
static void usage P_ ((FILE * stream, int status));
int
main (argc, argv)
int argc;
char **argv;
{
int optc;
int always_run_command = 1;
long orig_arg_max;
char *default_cmd = "/bin/echo";
int (*read_args) P_ ((void)) = read_line;
program_name = argv[0];
orig_arg_max = ARG_MAX - 2048; /* POSIX.2 requires subtracting 2048. */
arg_max = orig_arg_max;
/* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which
have it at 1 meg). Things will work fine with a large ARG_MAX but it
will probably hurt the system more than it needs to; an array of this
size is allocated. */
if (arg_max > 20 * 1024)
arg_max = 20 * 1024;
/* Take the size of the environment into account. */
arg_max -= env_size (environ);
if (arg_max <= 0)
error (1, 0, "environment is too large for exec");
while ((optc = getopt_long (argc, argv, "+0e::i::l::n:prs:txP:",
longopts, (int *) 0)) != -1)
{
switch (optc)
{
case '0':
read_args = read_string;
break;
case 'e':
if (optarg)
eof_str = optarg;
else
eof_str = 0;
break;
case 'h':
usage (stdout, 0);
case 'i':
if (optarg)
replace_pat = optarg;
else
replace_pat = "{}";
/* -i excludes -n -l. */
args_per_exec = 0;
lines_per_exec = 0;
break;
case 'l':
if (optarg)
lines_per_exec = parse_num (optarg, 'l', 1L, -1L);
else
lines_per_exec = 1;
/* -l excludes -i -n. */
args_per_exec = 0;
r