Add s-, t-, x-flags to and audit xargs(1)

The flexible design already allowed to add these flags trivially.
Drop the -I and -L-flags, which are XSI-extensions.
The audit generally consisted of style-changes, dropping kitchen-
sink functions, updating the usage and using estrtonum instead of
strtol.
This commit is contained in:
FRIGN 2015-03-22 22:53:12 +01:00
parent 521f324319
commit 71adaed519
3 changed files with 104 additions and 57 deletions

2
README
View File

@ -87,7 +87,7 @@ The following tools are implemented ('*' == finished, '#' == UTF-8 support,
=*| uudecode yes none =*| uudecode yes none
=*| uuencode yes none =*| uuencode yes none
#*| wc yes none #*| wc yes none
= xargs no -I, -L, -p, -s, -t, -x =*| xargs no none (-p)
=*| yes non-posix none =*| yes non-posix none
The complement of sbase is ubase[1] which is Linux-specific and The complement of sbase is ubase[1] which is Linux-specific and

59
xargs.1
View File

@ -1,14 +1,15 @@
.Dd January 30, 2015 .Dd March 22, 2015
.Dt XARGS 1 .Dt XARGS 1
.Os sbase .Os sbase
.Sh NAME .Sh NAME
.Nm xargs .Nm xargs
.Nd construct argument list(s) and execute command .Nd construct argument lists and execute command
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl n Ar maxargs .Op Fl rtx
.Op Fl r
.Op Fl E Ar eofstr .Op Fl E Ar eofstr
.Op Fl n Ar num
.Op Fl s Ar num
.Op Ar cmd Op Ar arg ... .Op Ar cmd Op Ar arg ...
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
@ -31,9 +32,9 @@ newlines, up to the matching double quote. Any single character, including
newlines, may be escaped by a backslash. newlines, may be escaped by a backslash.
.Sh OPTIONS .Sh OPTIONS
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl n Ar maxargs .It Fl n Ar num
Use at most Use at most
.Ar maxargs .Ar num
arguments per command line. arguments per command line.
.It Fl r .It Fl r
Do not run the command if there are no arguments. Normally the command is Do not run the command if there are no arguments. Normally the command is
@ -42,22 +43,54 @@ executed at least once even if there are no arguments.
Use Use
.Ar eofstr .Ar eofstr
as a logical EOF marker. as a logical EOF marker.
.It Fl s Ar num
Use at most
.Ar num
bytes per command line.
.It Fl t
Write the command line to stderr before executing it.
.It Fl x
Terminate if the command line exceeds the system limit or the number of bytes
given with the
.Op Fl s
flag.
.El .El
.Sh EXIT STATUS .Sh EXIT STATUS
xargs exits with one of the following values: .Nm
exits with one of the following values:
.Bl -tag -width Ds .Bl -tag -width Ds
.It 0 .It 0
All invocations of command returned a zero exit status. All invocations of
.Ar cmd
returned a zero exit status.
.It 123 .It 123
One or more invocations of command returned a nonzero exit status. One or more invocations of
.Ar cmd
returned a nonzero exit status.
.It 124 .It 124
The command exited with a 255 exit status. .Ar cmd
exited with a 255 exit status.
.It 125 .It 125
The command was killed or stopped by a signal. .Ar cmd
was killed or stopped by a signal.
.It 126 .It 126
The command was found but could not be executed. .Ar cmd
was found but could not be executed.
.It 127 .It 127
The command could not be found. .Ar cmd
could not be found.
.It 1 .It 1
Some other error occurred. Some other error occurred.
.El .El
.Sh STANDARDS
The
.Nm
utility is compliant with the
.St -p1003.1-2008
specification except from prompting with the
.Op Fl p
flag.
.Pp
The
.Op Fl r
flag is an extension to that specification.

94
xargs.c
View File

@ -2,6 +2,8 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <errno.h> #include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -9,12 +11,9 @@
#include "util.h" #include "util.h"
enum { #define NARGS 10000
NARGS = 10000
};
static int inputc(void); static int inputc(void);
static void deinputc(int);
static void fillargbuf(int); static void fillargbuf(int);
static int eatspace(void); static int eatspace(void);
static int parsequote(int); static int parsequote(int);
@ -23,14 +22,14 @@ static char *poparg(void);
static void waitchld(void); static void waitchld(void);
static void spawn(void); static void spawn(void);
static char *cmd[NARGS];
static char *argb;
static size_t argbsz; static size_t argbsz;
static size_t argbpos; static size_t argbpos;
static long maxargs = 0; static size_t maxargs = 0;
static int nerrors = 0; static int nerrors = 0;
static int rflag = 0, nflag = 0, tflag = 0, xflag = 0;
static char *argb;
static char *cmd[NARGS];
static char *eofstr; static char *eofstr;
static int rflag = 0, nflag = 0;
static int static int
inputc(void) inputc(void)
@ -39,14 +38,9 @@ inputc(void)
ch = getc(stdin); ch = getc(stdin);
if (ch == EOF && ferror(stdin)) if (ch == EOF && ferror(stdin))
eprintf("stdin: read error:"); eprintf("getc <stdin>:");
return ch;
}
static void return ch;
deinputc(int ch)
{
ungetc(ch, stdin);
} }
static void static void
@ -69,7 +63,7 @@ eatspace(void)
case ' ': case '\t': case '\n': case ' ': case '\t': case '\n':
break; break;
default: default:
deinputc(ch); ungetc(ch, stdin);
return ch; return ch;
} }
} }
@ -89,6 +83,7 @@ parsequote(int q)
argbpos++; argbpos++;
} }
} }
return -1; return -1;
} }
@ -102,6 +97,7 @@ parseescape(void)
argbpos++; argbpos++;
return ch; return ch;
} }
return -1; return -1;
} }
@ -137,9 +133,8 @@ poparg(void)
} }
out: out:
fillargbuf('\0'); fillargbuf('\0');
if (eofstr && strcmp(argb, eofstr) == 0)
return NULL; return (eofstr && !strcmp(argb, eofstr)) ? NULL : argb;
return argb;
} }
static void static void
@ -154,7 +149,7 @@ waitchld(void)
if (WEXITSTATUS(status) == 127 || if (WEXITSTATUS(status) == 127 ||
WEXITSTATUS(status) == 126) WEXITSTATUS(status) == 126)
exit(WEXITSTATUS(status)); exit(WEXITSTATUS(status));
if (status != 0) if (status)
nerrors++; nerrors++;
} }
if (WIFSIGNALED(status)) if (WIFSIGNALED(status))
@ -165,6 +160,15 @@ static void
spawn(void) spawn(void)
{ {
int savederrno; int savederrno;
char **p;
if (tflag) {
for (p = cmd; *p; p++) {
fputs(*p, stderr);
fputc(' ', stderr);
}
fputc('\n', stderr);
}
switch (fork()) { switch (fork()) {
case -1: case -1:
@ -181,26 +185,40 @@ spawn(void)
static void static void
usage(void) usage(void)
{ {
eprintf("usage: %s [-n maxargs] [-r] [-E eofstr] [cmd [arg...]]\n", argv0); eprintf("usage: %s [-rtx] [-E eofstr] [-n num] [-s num] [cmd [arg ...]]\n", argv0);
} }
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
int leftover = 0; int leftover = 0;
long argsz, argmaxsz; size_t argsz, argmaxsz;
char *arg = ""; char *arg = "";
int i, a; int i, a;
argmaxsz = sysconf(_SC_ARG_MAX);
if (argmaxsz < 0)
eprintf("sysconf:");
/* Leave some room for environment variables */
argmaxsz -= 4 * 1024;
ARGBEGIN { ARGBEGIN {
case 'n': case 'n':
nflag = 1; nflag = 1;
if ((maxargs = strtol(EARGF(usage()), NULL, 10)) <= 0) maxargs = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
eprintf("%s: value for -n option should be >= 1\n", argv0);
break; break;
case 'r': case 'r':
rflag = 1; rflag = 1;
break; break;
case 's':
argmaxsz = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
break;
case 't':
tflag = 1;
break;
case 'x':
xflag = 1;
break;
case 'E': case 'E':
eofstr = EARGF(usage()); eofstr = EARGF(usage());
break; break;
@ -208,15 +226,9 @@ main(int argc, char *argv[])
usage(); usage();
} ARGEND; } ARGEND;
argmaxsz = sysconf(_SC_ARG_MAX);
if (argmaxsz < 0)
eprintf("sysconf:");
/* Leave some room for environment variables */
argmaxsz -= 4 * 1024;
do { do {
argsz = 0; i = 0; a = 0; argsz = 0; i = 0; a = 0;
if (argc > 0) { if (argc) {
for (; i < argc; i++) { for (; i < argc; i++) {
cmd[i] = estrdup(argv[i]); cmd[i] = estrdup(argv[i]);
argsz += strlen(cmd[i]) + 1; argsz += strlen(cmd[i]) + 1;
@ -226,11 +238,13 @@ main(int argc, char *argv[])
argsz += strlen(cmd[i]) + 1; argsz += strlen(cmd[i]) + 1;
i++; i++;
} }
while (leftover == 1 || (arg = poparg())) { while (leftover || (arg = poparg())) {
if (argsz + strlen(arg) + 1 > argmaxsz || if (argsz + strlen(arg) + 1 > argmaxsz || i >= NARGS - 1) {
i >= NARGS - 1) { if (strlen(arg) + 1 > argmaxsz) {
if (strlen(arg) + 1 > argmaxsz) weprintf("insufficient argument space\n");
eprintf("insufficient argument space\n"); if (xflag)
exit(1);
}
leftover = 1; leftover = 1;
break; break;
} }
@ -239,13 +253,13 @@ main(int argc, char *argv[])
i++; i++;
a++; a++;
leftover = 0; leftover = 0;
if (nflag == 1 && a >= maxargs) if (nflag && a >= maxargs)
break; break;
} }
cmd[i] = NULL; cmd[i] = NULL;
if (a >= maxargs && nflag == 1) if (a >= maxargs && nflag)
spawn(); spawn();
else if (!a || (i == 1 && rflag == 1)) else if (!a || (i == 1 && rflag))
; ;
else else
spawn(); spawn();
@ -255,5 +269,5 @@ main(int argc, char *argv[])
free(argb); free(argb);
return nerrors > 0 ? 123 : 0; return nerrors ? 123 : 0;
} }