#if 0
#ifndef lint
static const char copyright[] =
"@(#) Copyright (c) 1990, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif
#ifndef lint
static char sccsid[] = "@(#)xargs.c 8.1 (Berkeley) 6/6/93";
#endif
#endif
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/usr.bin/xargs/xargs.c,v 1.57 2005/02/27 02:01:31 gad Exp $");
#include <sys/param.h>
#include <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <langinfo.h>
#include <locale.h>
#include <paths.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "pathnames.h"
#ifdef __APPLE__
#include <get_compat.h>
#else
#define COMPAT_MODE(a,b) (1)
#endif
static void parse_input(int, char *[]);
static void prerun(int, char *[]);
static int prompt(void);
static void run(char **);
static void usage(void);
void strnsubst(char **, const char *, const char *, size_t);
static void waitchildren(const char *, int);
static int last_was_newline = 1;
static int last_was_blank = 0;
static char echo[] = _PATH_ECHO;
static char **av, **bxp, **ep, **endxp, **xp;
static char *argp, *bbp, *ebp, *inpline, *p, *replstr;
static const char *eofstr;
static int count, insingle, indouble, oflag, pflag, tflag, Rflag, rval, zflag;
static int cnt, Iflag, jfound, Lflag, wasquoted, xflag;
static int curprocs, maxprocs;
static size_t pad9314053;
static volatile int childerr;
extern char **environ;
int
main(int argc, char *argv[])
{
long arg_max;
int ch, Jflag, nflag, nline;
size_t nargs;
size_t linelen;
char *endptr;
inpline = replstr = NULL;
ep = environ;
eofstr = "";
Jflag = nflag = 0;
(void)setlocale(LC_ALL, "");
nargs = 5000;
if ((arg_max = sysconf(_SC_ARG_MAX)) == -1)
errx(1, "sysconf(_SC_ARG_MAX) failed");
nline = arg_max - MAXPATHLEN;
pad9314053 = sizeof(char *);
while (*ep != NULL) {
nline -= strlen(*ep++) + 1 + sizeof(*ep);
}
nline -= pad9314053;
maxprocs = 1;
while ((ch = getopt(argc, argv, "0E:I:J:L:n:oP:pR:s:tx")) != -1)
switch(ch) {
case 'E':
eofstr = optarg;
break;
case 'I':
Jflag = 0;
Iflag = 1;
Lflag = 1;
replstr = optarg;
break;
case 'J':
Iflag = 0;
Jflag = 1;
replstr = optarg;
break;
case 'L':
Lflag = atoi(optarg);
if (COMPAT_MODE("bin/xargs", "Unix2003")) {
nflag = 0;
nargs = 5000;
}
break;
case 'n':
nflag = 1;
if ((nargs = strtol(optarg, NULL, 10)) <= 0)
errx(1, "illegal argument count");
if (COMPAT_MODE("bin/xargs", "Unix2003")) {
Lflag = 0;
}
break;
case 'o':
oflag = 1;
break;
case 'P':
if ((maxprocs = atoi(optarg)) <= 0)
errx(1, "max. processes must be >0");
break;
case 'p':
pflag = 1;
break;
case 'R':
Rflag = strtol(optarg, &endptr, 10);
if (*endptr != '\0')
errx(1, "replacements must be a number");
break;
case 's':
nline = atoi(optarg);
pad9314053 = 0;
break;
case 't':
tflag = 1;
break;
case 'x':
xflag = 1;
break;
case '0':
zflag = 1;
break;
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
if (!Iflag && Rflag)
usage();
if (Iflag && !Rflag)
Rflag = 5;
if (xflag && !nflag)
usage();
if (Iflag || Lflag)
xflag = 1;
if (replstr != NULL && *replstr == '\0')
errx(1, "replstr may not be empty");
linelen = 1 + argc + nargs + 1;
if ((av = bxp = malloc(linelen * sizeof(char **))) == NULL)
errx(1, "malloc failed");
if (*argv == NULL)
cnt = strlen(*bxp++ = echo) + pad9314053;
else {
do {
if (Jflag && strcmp(*argv, replstr) == 0) {
char **avj;
jfound = 1;
argv++;
for (avj = argv; *avj; avj++)
cnt += strlen(*avj) + 1 + pad9314053;
break;
}
cnt += strlen(*bxp++ = *argv) + 1 + pad9314053;
} while (*++argv != NULL);
}
endxp = (xp = bxp) + nargs;
nline -= cnt;
if (nline <= 0)
errx(1, "insufficient space for command");
if ((bbp = malloc((size_t)(nline + 1))) == NULL)
errx(1, "malloc failed");
ebp = (argp = p = bbp) + nline - 1;
for (;;)
parse_input(argc, argv);
}
static void
parse_input(int argc, char *argv[])
{
int ch, foundeof;
char **avj;
int last_was_backslashed = 0;
foundeof = 0;
switch(ch = getchar()) {
case EOF:
if (p == bbp) {
waitchildren(*argv, 1);
exit(rval);
}
goto arg1;
case ' ':
last_was_blank = 1;
case '\t':
if (insingle || indouble || zflag)
goto addch;
goto arg2;
case '\0':
if (zflag) {
count++;
goto arg2;
}
goto addch;
case '\n':
if (zflag)
goto addch;
if (COMPAT_MODE("bin/xargs", "Unix2003")) {
if (last_was_newline) {
break;
}
if (!last_was_blank ) {
count++;
}
} else {
count++;
}
last_was_newline = 1;
arg1: if (insingle || indouble)
errx(1, "unterminated quote");
arg2:
foundeof = *eofstr != '\0' &&
strcmp(argp, eofstr) == 0;
#ifdef __APPLE__
if (Iflag && !last_was_newline)
foundeof = 0;
if (foundeof && (p - strlen(eofstr) == bbp)) {
waitchildren(*argv, 1);
exit(rval);
}
#endif
if ((argp != p || wasquoted) && !foundeof) {
*p++ = '\0';
*xp++ = argp;
if (Iflag) {
size_t curlen;
if (inpline == NULL)
curlen = 0;
else {
if ((curlen = strlen(inpline)))
strcat(inpline, " ");
}
curlen++;
inpline = realloc(inpline, curlen + 2 +
strlen(argp));
if (inpline == NULL)
errx(1, "realloc failed");
if (curlen == 1)
strcpy(inpline, argp);
else
strcat(inpline, argp);
}
}
if (xp == endxp || p + (count * pad9314053) > ebp || ch == EOF ||
(Lflag <= count && xflag) || foundeof) {
if (xflag && xp != endxp && p + (count * pad9314053) > ebp)
errx(1, "insufficient space for arguments");
if (jfound) {
for (avj = argv; *avj; avj++)
*xp++ = *avj;
}
prerun(argc, av);
if (ch == EOF || foundeof) {
waitchildren(*argv, 1);
exit(rval);
}
p = bbp;
xp = bxp;
count = 0;
}
argp = p;
wasquoted = 0;
break;
case '\'':
if (indouble || zflag)
goto addch;
insingle = !insingle;
wasquoted = 1;
break;
case '"':
if (insingle || zflag)
goto addch;
indouble = !indouble;
wasquoted = 1;
break;
case '\\':
last_was_backslashed = 1;
if (zflag)
goto addch;
if (!insingle && !indouble && (ch = getchar()) == EOF)
errx(1, "backslash at EOF");
default:
addch: if (p < ebp) {
*p++ = ch;
break;
}
if (bxp == xp)
errx(1, "insufficient space for argument");
if (xflag)
errx(1, "insufficient space for arguments");
if (jfound) {
for (avj = argv; *avj; avj++)
*xp++ = *avj;
}
prerun(argc, av);
xp = bxp;
cnt = ebp - argp;
memcpy(bbp, argp, (size_t)cnt);
p = (argp = bbp) + cnt;
*p++ = ch;
break;
}
if (ch != ' ')
last_was_blank = 0;
if (ch != '\n' || last_was_backslashed)
last_was_newline = 0;
}
static void
prerun(int argc, char *argv[])
{
char **tmp, **tmp2, **avj;
int repls;
repls = Rflag;
if (argc == 0 || repls == 0) {
*xp = NULL;
run(argv);
return;
}
avj = argv;
tmp = malloc((argc + 1) * sizeof(char**));
if (tmp == NULL)
errx(1, "malloc failed");
tmp2 = tmp;
if ((*tmp++ = strdup(*avj++)) == NULL)
errx(1, "strdup failed");
while (--argc) {
*tmp = *avj++;
if (repls && strstr(*tmp, replstr) != NULL) {
strnsubst(tmp++, replstr, inpline, (size_t)255);
if (repls > 0)
repls--;
} else {
if ((*tmp = strdup(*tmp)) == NULL)
errx(1, "strdup failed");
tmp++;
}
}
*tmp = NULL;
run(tmp2);
for (; tmp2 != tmp; tmp--)
free(*tmp);
free(tmp2);
if (inpline != NULL) {
free(inpline);
inpline = NULL;
}
}
static void
run(char **argv)
{
pid_t pid;
int fd;
char **avec;
if (tflag || pflag) {
(void)fprintf(stderr, "%s", *argv);
for (avec = argv + 1; *avec != NULL; ++avec)
(void)fprintf(stderr, " %s", *avec);
if (pflag)
switch (prompt()) {
case 0:
return;
case 1:
goto exec;
case 2:
break;
}
(void)fprintf(stderr, "\n");
(void)fflush(stderr);
}
exec:
childerr = 0;
switch(pid = vfork()) {
case -1:
err(1, "vfork");
case 0:
if (oflag) {
if ((fd = open(_PATH_TTY, O_RDONLY)) == -1)
err(1, "can't open /dev/tty");
} else {
fd = open(_PATH_DEVNULL, O_RDONLY);
}
if (fd > STDIN_FILENO) {
if (dup2(fd, STDIN_FILENO) != 0)
err(1, "can't dup2 to stdin");
close(fd);
}
execvp(argv[0], argv);
childerr = errno;
_exit(1);
}
curprocs++;
waitchildren(*argv, 0);
}
static void
waitchildren(const char *name, int waitall)
{
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, !waitall && curprocs < maxprocs ?
WNOHANG : 0)) > 0) {
curprocs--;
if (childerr != 0) {
errno = childerr;
err(errno == ENOENT ? 127 : 126, "%s", name);
}
if (WIFSIGNALED(status) || WEXITSTATUS(status) == 255)
exit(1);
if (WEXITSTATUS(status))
rval = 1;
}
if (pid == -1 && errno != ECHILD)
err(1, "wait3");
}
static int
prompt(void)
{
regex_t cre;
size_t rsize;
int match;
char *response;
FILE *ttyfp;
if ((ttyfp = fopen(_PATH_TTY, "r")) == NULL)
return (2);
(void)fprintf(stderr, "?...");
(void)fflush(stderr);
if ((response = fgetln(ttyfp, &rsize)) == NULL ||
regcomp(&cre, nl_langinfo(YESEXPR), REG_BASIC) != 0) {
(void)fclose(ttyfp);
return (0);
}
match = regexec(&cre, response, 0, NULL, 0);
(void)fclose(ttyfp);
regfree(&cre);
return (match == 0);
}
static void
usage(void)
{
fprintf(stderr,
"usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]\n"
" [-L number] [-n number [-x]] [-P maxprocs] [-s size]\n"
" [utility [argument ...]]\n");
exit(1);
}