aboutsummaryrefslogtreecommitdiff
path: root/findutils/xargs.c
diff options
context:
space:
mode:
authorEric Andersen <andersen@codepoet.org>2000-09-22 20:01:23 +0000
committerEric Andersen <andersen@codepoet.org>2000-09-22 20:01:23 +0000
commit92a61c1206572f4a6e55baa24e7cdd4f180d4b64 (patch)
treec5354bb550d20972523bfb5c38b8666df527d75d /findutils/xargs.c
parent90f580ad5bde613e01a5ddf2efb5eb2d3a5f6a72 (diff)
downloadbusybox-w32-92a61c1206572f4a6e55baa24e7cdd4f180d4b64.tar.gz
busybox-w32-92a61c1206572f4a6e55baa24e7cdd4f180d4b64.tar.bz2
busybox-w32-92a61c1206572f4a6e55baa24e7cdd4f180d4b64.zip
Add in xargs
Diffstat (limited to 'findutils/xargs.c')
-rw-r--r--findutils/xargs.c922
1 files changed, 922 insertions, 0 deletions
diff --git a/findutils/xargs.c b/findutils/xargs.c
new file mode 100644
index 000000000..73b1a0237
--- /dev/null
+++ b/findutils/xargs.c
@@ -0,0 +1,922 @@
1/* xargs for busybox */
2
3/* xargs -- build and execute command lines from standard input
4 Copyright (C) 1990, 91, 92, 93, 94 Free Software Foundation, Inc.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20/* Written by Mike Rendell <michael@cs.mun.ca>
21 and David MacKenzie <djm@gnu.ai.mit.edu>. */
22
23#include "internal.h"
24
25#define HAVE_STRING_H 1
26#define HAVE_SYS_WAIT_H 1
27#define HAVE_UNISTD_H 1
28#define HAVE_LIMITS_H 1
29#define STDC_HEADERS 1
30
31#include <sys/types.h> /* For pid_t. */
32#if HAVE_SYS_WAIT_H
33#include <sys/wait.h>
34#endif
35
36#ifndef WIFSTOPPED
37#define WIFSTOPPED(w) (((w) & 0xff) == 0x7f)
38#endif
39#ifndef WIFSIGNALED
40#define WIFSIGNALED(w) (((w) & 0xff) != 0x7f && ((w) & 0xff) != 0)
41#endif
42#ifndef WIFEXITED
43#define WIFEXITED(w) (((w) & 0xff) == 0)
44#endif
45
46#ifndef WSTOPSIG
47#define WSTOPSIG(w) (((w) >> 8) & 0xff)
48#endif
49#ifndef WTERMSIG
50#define WTERMSIG(w) ((w) & 0x7f)
51#endif
52#ifndef WEXITSTATUS
53#define WEXITSTATUS(w) (((w) >> 8) & 0xff)
54#endif
55
56#if __STDC__
57#define P_(s) s
58#else
59#define P_(s) ()
60#endif
61
62#include <ctype.h>
63
64#if !defined (isascii) || defined (STDC_HEADERS)
65#ifdef isascii
66#undef isascii
67#endif
68#define isascii(c) 1
69#endif
70
71#ifdef isblank
72#define ISBLANK(c) (isascii (c) && isblank (c))
73#else
74#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
75#endif
76
77#define ISSPACE(c) (ISBLANK (c) || (c) == '\n' || (c) == '\r' \
78 || (c) == '\f' || (c) == '\v')
79
80#include <stdio.h>
81#include <errno.h>
82#include <getopt.h>
83
84#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
85#include <string.h>
86#if !defined(STDC_HEADERS)
87#include <memory.h>
88#endif
89#else
90#include <strings.h>
91#define memcpy(dest, source, count) (bcopy((source), (dest), (count)))
92#endif
93
94#ifndef _POSIX_SOURCE
95#include <sys/param.h>
96#endif
97
98#ifdef HAVE_LIMITS_H
99#include <limits.h>
100#endif
101
102#ifndef LONG_MAX
103#define LONG_MAX (~(1 << (sizeof (long) * 8 - 1)))
104#endif
105
106#ifdef HAVE_UNISTD_H
107#include <unistd.h>
108#endif
109
110#include <signal.h>
111
112#if !defined(SIGCHLD) && defined(SIGCLD)
113#define SIGCHLD SIGCLD
114#endif
115
116/* COMPAT: SYSV version defaults size (and has a max value of) to 470.
117 We try to make it as large as possible. */
118#if !defined(ARG_MAX) && defined(_SC_ARG_MAX)
119#define ARG_MAX sysconf (_SC_ARG_MAX)
120#endif
121#ifndef ARG_MAX
122#define ARG_MAX NCARGS
123#endif
124
125/* States for read_line. */
126#define NORM 0
127#define SPACE 1
128#define QUOTE 2
129#define BACKSLASH 3
130
131#ifdef STDC_HEADERS
132#include <stdlib.h>
133#else
134extern int errno;
135#endif
136
137/* Return nonzero if S is the EOF string. */
138#define EOF_STR(s) (eof_str && *eof_str == *s && !strcmp (eof_str, s))
139
140extern char **environ;
141
142/* Not char because of type promotion; NeXT gcc can't handle it. */
143typedef int boolean;
144#define true 1
145#define false 0
146
147#if __STDC__
148#define VOID void
149#else
150#define VOID char
151#endif
152
153VOID *xmalloc P_ ((size_t n));
154VOID *xrealloc P_ ((VOID * p, size_t n));
155
156/* The name this program was run with. */
157char *program_name;
158
159/* Buffer for reading arguments from stdin. */
160static char *linebuf;
161
162/* Line number in stdin since the last command was executed. */
163static int lineno = 0;
164
165/* If nonzero, then instead of putting the args from stdin at
166 the end of the command argument list, they are each stuck into the
167 initial args, replacing each occurrence of the `replace_pat' in the
168 initial args. */
169static char *replace_pat = NULL;
170
171/* The length of `replace_pat'. */
172static size_t rplen = 0;
173
174/* If nonzero, when this string is read on stdin it is treated as
175 end of file.
176 I don't like this - it should default to NULL. */
177static char *eof_str = "_";
178
179/* If nonzero, the maximum number of nonblank lines from stdin to use
180 per command line. */
181static long lines_per_exec = 0;
182
183/* The maximum number of arguments to use per command line. */
184static long args_per_exec = 1024;
185
186/* If true, exit if lines_per_exec or args_per_exec is exceeded. */
187static boolean exit_if_size_exceeded = false;
188
189/* The maximum number of characters that can be used per command line. */
190static long arg_max;
191
192/* Storage for elements of `cmd_argv'. */
193static char *argbuf;
194
195/* The list of args being built. */
196static char **cmd_argv = NULL;
197
198/* Number of elements allocated for `cmd_argv'. */
199static int cmd_argv_alloc = 0;
200
201/* Number of valid elements in `cmd_argv'. */
202static int cmd_argc = 0;
203
204/* Number of chars being used in `cmd_argv'. */
205static int cmd_argv_chars = 0;
206
207/* Number of initial arguments given on the command line. */
208static int initial_argc = 0;
209
210/* Number of chars in the initial args. */
211static int initial_argv_chars = 0;
212
213/* true when building up initial arguments in `cmd_argv'. */
214static boolean initial_args = true;
215
216/* If nonzero, the maximum number of child processes that can be running
217 at once. */
218static int proc_max = 1;
219
220/* Total number of child processes that have been executed. */
221static int procs_executed = 0;
222
223/* The number of elements in `pids'. */
224static int procs_executing = 0;
225
226/* List of child processes currently executing. */
227static pid_t *pids = NULL;
228
229/* The number of allocated elements in `pids'. */
230static int pids_alloc = 0;
231
232/* Exit status; nonzero if any child process exited with a
233 status of 1-125. */
234static int child_error = 0;
235
236/* If true, print each command on stderr before executing it. */
237static boolean print_command = false;
238
239/* If true, query the user before executing each command, and only
240 execute the command if the user responds affirmatively. */
241static boolean query_before_executing = false;
242
243static struct option const longopts[] =
244{
245 {"null", no_argument, NULL, '0'},
246 {"eof", optional_argument, NULL, 'e'},
247 {"replace", optional_argument, NULL, 'i'},
248 {"max-lines", optional_argument, NULL, 'l'},
249 {"max-args", required_argument, NULL, 'n'},
250 {"interactive", no_argument, NULL, 'p'},
251 {"no-run-if-empty", no_argument, NULL, 'r'},
252 {"max-chars", required_argument, NULL, 's'},
253 {"verbose", no_argument, NULL, 't'},
254 {"exit", no_argument, NULL, 'x'},
255 {"max-procs", required_argument, NULL, 'P'},
256 {"help", no_argument, NULL, 'h'},
257 {NULL, no_argument, NULL, 0}
258};
259
260static int read_line P_ ((void));
261static int read_string P_ ((void));
262static void do_insert P_ ((char *arg, size_t arglen, size_t lblen));
263static void push_arg P_ ((char *arg, size_t len));
264static boolean print_args P_ ((boolean ask));
265static void do_exec P_ ((void));
266static void add_proc P_ ((pid_t pid));
267static void wait_for_proc P_ ((boolean all));
268static long parse_num P_ ((char *str, int option, long min, long max));
269static long env_size P_ ((char **envp));
270
271int xargs_main (argc, argv)
272 int argc;
273 char **argv;
274{
275 int optc;
276 int always_run_command = 1;
277 long orig_arg_max;
278 char *default_cmd = "/bin/echo";
279 int (*read_args) P_ ((void)) = read_line;
280
281 program_name = argv[0];
282
283 orig_arg_max = ARG_MAX;
284 if (orig_arg_max == -1)
285 orig_arg_max = LONG_MAX;
286 orig_arg_max -= 2048; /* POSIX.2 requires subtracting 2048. */
287 arg_max = orig_arg_max;
288
289 /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which
290 have it at 1 meg). Things will work fine with a large ARG_MAX but it
291 will probably hurt the system more than it needs to; an array of this
292 size is allocated. */
293 if (arg_max > 20 * 1024)
294 arg_max = 20 * 1024;
295
296 /* Take the size of the environment into account. */
297 arg_max -= env_size (environ);
298 if (arg_max <= 0)
299 fatalError("environment is too large for exec");
300
301 while ((optc = getopt_long (argc, argv, "+0e::i::l::n:prs:txP:",
302 longopts, (int *) 0)) != -1)
303 {
304 switch (optc)
305 {
306 case '0':
307 read_args = read_string;
308 break;
309
310 case 'e':
311 if (optarg)
312 eof_str = optarg;
313 else
314 eof_str = 0;
315 break;
316
317 case 'h':
318 usage (xargs_usage);
319
320 case 'i':
321 if (optarg)
322 replace_pat = optarg;
323 else
324 replace_pat = "{}";
325 /* -i excludes -n -l. */
326 args_per_exec = 0;
327 lines_per_exec = 0;
328 break;
329
330 case 'l':
331 if (optarg)
332 lines_per_exec = parse_num (optarg, 'l', 1L, -1L);
333 else
334 lines_per_exec = 1;
335 /* -l excludes -i -n. */
336 args_per_exec = 0;
337 replace_pat = NULL;
338 break;
339
340 case 'n':
341 args_per_exec = parse_num (optarg, 'n', 1L, -1L);
342 /* -n excludes -i -l. */
343 lines_per_exec = 0;
344 replace_pat = NULL;
345 break;
346
347 case 's':
348 arg_max = parse_num (optarg, 's', 1L, orig_arg_max);
349 break;
350
351 case 't':
352 print_command = true;
353 break;
354
355 case 'x':
356 exit_if_size_exceeded = true;
357 break;
358
359 case 'p':
360 query_before_executing = true;
361 print_command = true;
362 break;
363
364 case 'r':
365 always_run_command = 0;
366 break;
367
368 case 'P':
369 proc_max = parse_num (optarg, 'P', 0L, -1L);
370 break;
371
372 default:
373 usage (xargs_usage);
374 }
375 }
376
377 if (replace_pat || lines_per_exec)
378 exit_if_size_exceeded = true;
379
380 if (optind == argc)
381 {
382 optind = 0;
383 argc = 1;
384 argv = &default_cmd;
385 }
386
387 linebuf = (char *) xmalloc (arg_max + 1);
388 argbuf = (char *) xmalloc (arg_max + 1);
389
390 /* Make sure to listen for the kids. */
391 signal (SIGCHLD, SIG_DFL);
392
393 if (!replace_pat)
394 {
395 for (; optind < argc; optind++)
396 push_arg (argv[optind], strlen (argv[optind]) + 1);
397 initial_args = false;
398 initial_argc = cmd_argc;
399 initial_argv_chars = cmd_argv_chars;
400
401 while ((*read_args) () != -1)
402 if (lines_per_exec && lineno >= lines_per_exec)
403 {
404 do_exec ();
405 lineno = 0;
406 }
407
408 /* SYSV xargs seems to do at least one exec, even if the
409 input is empty. */
410 if (cmd_argc != initial_argc
411 || (always_run_command && procs_executed == 0))
412 do_exec ();
413 }
414 else
415 {
416 int i;
417 size_t len;
418 size_t *arglen = (size_t *) xmalloc (sizeof (size_t) * argc);
419
420 for (i = optind; i < argc; i++)
421 arglen[i] = strlen(argv[i]);
422 rplen = strlen (replace_pat);
423 while ((len = (*read_args) ()) != -1)
424 {
425 /* Don't do insert on the command name. */
426 push_arg (argv[optind], arglen[optind] + 1);
427 len--;
428 for (i = optind + 1; i < argc; i++)
429 do_insert (argv[i], arglen[i], len);
430 do_exec ();
431 }
432 }
433
434 wait_for_proc (true);
435 exit (child_error);
436}
437
438/* Read a line of arguments from stdin and add them to the list of
439 arguments to pass to the command. Ignore blank lines and initial blanks.
440 Single and double quotes and backslashes quote metacharacters and blanks
441 as they do in the shell.
442 Return -1 if eof (either physical or logical) is reached,
443 otherwise the length of the last string read (including the null). */
444
445static int
446read_line ()
447{
448 static boolean eof = false;
449 /* Start out in mode SPACE to always strip leading spaces (even with -i). */
450 int state = SPACE; /* The type of character we last read. */
451 int prevc; /* The previous value of c. */
452 int quotc = 0; /* The last quote character read. */
453 int c = EOF;
454 boolean first = true; /* true if reading first arg on line. */
455 int len;
456 char *p = linebuf;
457 /* Including the NUL, the args must not grow past this point. */
458 char *endbuf = linebuf + arg_max - initial_argv_chars - 1;
459
460 if (eof)
461 return -1;
462 while (1)
463 {
464 prevc = c;
465 c = getc (stdin);
466 if (c == EOF)
467 {
468 /* COMPAT: SYSV seems to ignore stuff on a line that
469 ends without a \n; we don't. */
470 eof = true;
471 if (p == linebuf)
472 return -1;
473 *p++ = '\0';
474 len = p - linebuf;
475 /* FIXME we don't check for unterminated quotes here. */
476 if (first && EOF_STR (linebuf))
477 return -1;
478 if (!replace_pat)
479 push_arg (linebuf, len);
480 return len;
481 }
482 switch (state)
483 {
484 case SPACE:
485 if (ISSPACE (c))
486 continue;
487 state = NORM;
488 /* aaahhhh.... */
489
490 case NORM:
491 if (c == '\n')
492 {
493 if (!ISBLANK (prevc))
494 lineno++; /* For -l. */
495 if (p == linebuf)
496 {
497 /* Blank line. */
498 state = SPACE;
499 continue;
500 }
501 *p++ = '\0';
502 len = p - linebuf;
503 if (EOF_STR (linebuf))
504 {
505 eof = true;
506 return first ? -1 : len;
507 }
508 if (!replace_pat)
509 push_arg (linebuf, len);
510 return len;
511 }
512 if (!replace_pat && ISSPACE (c))
513 {
514 *p++ = '\0';
515 len = p - linebuf;
516 if (EOF_STR (linebuf))
517 {
518 eof = true;
519 return first ? -1 : len;
520 }
521 push_arg (linebuf, len);
522 p = linebuf;
523 state = SPACE;
524 first = false;
525 continue;
526 }
527 switch (c)
528 {
529 case '\\':
530 state = BACKSLASH;
531 continue;
532
533 case '\'':
534 case '"':
535 state = QUOTE;
536 quotc = c;
537 continue;
538 }
539 break;
540
541 case QUOTE:
542 if (c == '\n')
543 fatalError ("unmatched %s quote", quotc == '"' ? "double" : "single");
544 if (c == quotc)
545 {
546 state = NORM;
547 continue;
548 }
549 break;
550
551 case BACKSLASH:
552 state = NORM;
553 break;
554 }
555 if (p >= endbuf)
556 fatalError ("argument line too long");
557 *p++ = c;
558 }
559}
560
561/* Read a null-terminated string from stdin and add it to the list of
562 arguments to pass to the command.
563 Return -1 if eof (either physical or logical) is reached,
564 otherwise the length of the string read (including the null). */
565
566static int
567read_string ()
568{
569 static boolean eof = false;
570 int len;
571 char *p = linebuf;
572 /* Including the NUL, the args must not grow past this point. */
573 char *endbuf = linebuf + arg_max - initial_argv_chars - 1;
574
575 if (eof)
576 return -1;
577 while (1)
578 {
579 int c = getc (stdin);
580 if (c == EOF)
581 {
582 eof = true;
583 if (p == linebuf)
584 return -1;
585 *p++ = '\0';
586 len = p - linebuf;
587 if (!replace_pat)
588 push_arg (linebuf, len);
589 return len;
590 }
591 if (c == '\0')
592 {
593 lineno++; /* For -l. */
594 *p++ = '\0';
595 len = p - linebuf;
596 if (!replace_pat)
597 push_arg (linebuf, len);
598 return len;
599 }
600 if (p >= endbuf)
601 fatalError ("argument line too long");
602 *p++ = c;
603 }
604}
605
606/* Replace all instances of `replace_pat' in ARG with `linebuf',
607 and add the resulting string to the list of arguments for the command
608 to execute.
609 ARGLEN is the length of ARG, not including the null.
610 LBLEN is the length of `linebuf', not including the null.
611
612 COMPAT: insertions on the SYSV version are limited to 255 chars per line,
613 and a max of 5 occurences of replace_pat in the initial-arguments.
614 Those restrictions do not exist here. */
615
616static void
617do_insert (arg, arglen, lblen)
618 char *arg;
619 size_t arglen;
620 size_t lblen;
621{
622 /* Temporary copy of each arg with the replace pattern replaced by the
623 real arg. */
624 static char *insertbuf;
625 char *p;
626 int bytes_left = arg_max - 1; /* Bytes left on the command line. */
627
628 if (!insertbuf)
629 insertbuf = (char *) xmalloc (arg_max + 1);
630 p = insertbuf;
631
632 do
633 {
634 size_t len; /* Length in ARG before `replace_pat'. */
635 char *s = strstr (arg, replace_pat);
636 if (s)
637 len = s - arg;
638 else
639 len = arglen;
640 bytes_left -= len;
641 if (bytes_left <= 0)
642 break;
643
644 strncpy (p, arg, len);
645 p += len;
646 arg += len;
647 arglen -= len;
648
649 if (s)
650 {
651 bytes_left -= lblen;
652 if (bytes_left <= 0)
653 break;
654 strcpy (p, linebuf);
655 arg += rplen;
656 arglen -= rplen;
657 p += lblen;
658 }
659 }
660 while (*arg);
661 if (*arg)
662 fatalError ("command too long");
663 *p++ = '\0';
664 push_arg (insertbuf, p - insertbuf);
665}
666
667/* Add ARG to the end of the list of arguments `cmd_argv' to pass
668 to the command.
669 LEN is the length of ARG, including the terminating null.
670 If this brings the list up to its maximum size, execute the command. */
671
672static void
673push_arg (arg, len)
674 char *arg;
675 size_t len;
676{
677 if (arg)
678 {
679 if (cmd_argv_chars + len > arg_max)
680 {
681 if (initial_args || cmd_argc == initial_argc)
682 fatalError ("can not fit single argument within argument list size limit");
683 if (replace_pat
684 || (exit_if_size_exceeded &&
685 (lines_per_exec || args_per_exec)))
686 fatalError ("argument list too long");
687 do_exec ();
688 }
689 if (!initial_args && args_per_exec &&
690 cmd_argc - initial_argc == args_per_exec)
691 do_exec ();
692 }
693
694 if (cmd_argc >= cmd_argv_alloc)
695 {
696 if (!cmd_argv)
697 {
698 cmd_argv_alloc = 64;
699 cmd_argv = (char **) xmalloc (sizeof (char *) * cmd_argv_alloc);
700 }
701 else
702 {
703 cmd_argv_alloc *= 2;
704 cmd_argv = (char **) xrealloc (cmd_argv,
705 sizeof (char *) * cmd_argv_alloc);
706 }
707 }
708
709 if (!arg)
710 cmd_argv[cmd_argc++] = NULL;
711 else
712 {
713 cmd_argv[cmd_argc++] = argbuf + cmd_argv_chars;
714 strcpy (argbuf + cmd_argv_chars, arg);
715 cmd_argv_chars += len;
716 }
717}
718
719/* Print the arguments of the command to execute.
720 If ASK is nonzero, prompt the user for a response, and
721 if the user responds affirmatively, return true;
722 otherwise, return false. */
723
724static boolean
725print_args (ask)
726 boolean ask;
727{
728 int i;
729
730 for (i = 0; i < cmd_argc - 1; i++)
731 fprintf (stderr, "%s ", cmd_argv[i]);
732 if (ask)
733 {
734 static FILE *tty_stream;
735 int c, savec;
736
737 if (!tty_stream)
738 {
739 tty_stream = fopen ("/dev/tty", "r");
740 if (!tty_stream)
741 fatalError (" Could not open /dev/tty");
742 }
743 fputs ("?...", stderr);
744 fflush (stderr);
745 c = savec = getc (tty_stream);
746 while (c != EOF && c != '\n')
747 c = getc (tty_stream);
748 if (savec == 'y' || savec == 'Y')
749 return true;
750 }
751 else
752 putc ('\n', stderr);
753
754 return false;
755}
756
757/* Execute the command that has been built in `cmd_argv'. This may involve
758 waiting for processes that were previously executed. */
759
760static void
761do_exec ()
762{
763 pid_t child;
764
765 push_arg ((char *) NULL, 0); /* Null terminate the arg list. */
766 if (!query_before_executing || print_args (true))
767 {
768 if (proc_max && procs_executing >= proc_max)
769 wait_for_proc (false);
770 if (!query_before_executing && print_command)
771 print_args (false);
772 /* If we run out of processes, wait for a child to return and
773 try again. */
774 while ((child = fork ()) < 0 && errno == EAGAIN && procs_executing)
775 wait_for_proc (false);
776 switch (child)
777 {
778 case -1:
779 fatalError ("cannot fork");
780
781 case 0: /* Child. */
782 execvp (cmd_argv[0], cmd_argv);
783 errorMsg ("failed to exec '%s'", cmd_argv[0]);
784 _exit (errno == ENOENT ? 127 : 126);
785 }
786 add_proc (child);
787 }
788
789 cmd_argc = initial_argc;
790 cmd_argv_chars = initial_argv_chars;
791}
792
793/* Add the process with id PID to the list of processes that have
794 been executed. */
795
796static void
797add_proc (pid)
798 pid_t pid;
799{
800 int i;
801
802 /* Find an empty slot. */
803 for (i = 0; i < pids_alloc && pids[i]; i++)
804 ;
805 if (i == pids_alloc)
806 {
807 if (pids_alloc == 0)
808 {
809 pids_alloc = proc_max ? proc_max : 64;
810 pids = (pid_t *) xmalloc (sizeof (pid_t) * pids_alloc);
811 }
812 else
813 {
814 pids_alloc *= 2;
815 pids = (pid_t *) xrealloc (pids,
816 sizeof (pid_t) * pids_alloc);
817 }
818 memset (&pids[i], '\0', sizeof (pid_t) * (pids_alloc - i));
819 }
820 pids[i] = pid;
821 procs_executing++;
822 procs_executed++;
823}
824
825/* If ALL is true, wait for all child processes to finish;
826 otherwise, wait for one child process to finish.
827 Remove the processes that finish from the list of executing processes. */
828
829static void
830wait_for_proc (all)
831 boolean all;
832{
833 while (procs_executing)
834 {
835 int i, status;
836
837 do
838 {
839 pid_t pid;
840
841 pid = wait (&status);
842 if (pid < 0)
843 fatalError ("error waiting for child process");
844
845 /* Find the entry in `pids' for the child process
846 that exited. */
847 for (i = 0; i < pids_alloc && pid != pids[i]; i++)
848 ;
849 }
850 while (i == pids_alloc); /* A child died that we didn't start? */
851
852 /* Remove the child from the list. */
853 pids[i] = 0;
854 procs_executing--;
855
856 if (WEXITSTATUS (status) == 126 || WEXITSTATUS (status) == 127)
857 exit (WEXITSTATUS (status)); /* Can't find or run the command. */
858 if (WEXITSTATUS (status) == 255)
859 fatalError ( "%s: exited with status 255; aborting", cmd_argv[0]);
860 if (WIFSTOPPED (status))
861 fatalError ( "%s: stopped by signal %d", cmd_argv[0], WSTOPSIG (status));
862 if (WIFSIGNALED (status))
863 fatalError ("%s: terminated by signal %d", cmd_argv[0], WTERMSIG (status));
864 if (WEXITSTATUS (status) != 0)
865 child_error = 123;
866
867 if (!all)
868 break;
869 }
870}
871
872/* Return the value of the number represented in STR.
873 OPTION is the command line option to which STR is the argument.
874 If the value does not fall within the boundaries MIN and MAX,
875 Print an error message mentioning OPTION and exit. */
876
877static long
878parse_num (str, option, min, max)
879 char *str;
880 int option;
881 long min;
882 long max;
883{
884 char *eptr;
885 long val;
886
887 val = strtol (str, &eptr, 10);
888 if (eptr == str || *eptr)
889 {
890 fprintf (stderr, "%s: invalid number for -%c option\n",
891 program_name, option);
892 usage (xargs_usage);
893 }
894 else if (val < min)
895 {
896 fprintf (stderr, "%s: value for -%c option must be >= %ld\n",
897 program_name, option, min);
898 usage (xargs_usage);
899 }
900 else if (max >= 0 && val > max)
901 {
902 fprintf (stderr, "%s: value for -%c option must be < %ld\n",
903 program_name, option, max);
904 usage (xargs_usage);
905 }
906 return val;
907}
908
909/* Return how much of ARG_MAX is used by the environment. */
910
911static long
912env_size (envp)
913 char **envp;
914{
915 long len = 0;
916
917 while (*envp)
918 len += strlen (*envp++) + 1;
919
920 return len;
921}
922