commit 71adaed51930c72971e202701e3dce860291f178
parent 521f3243193c98d84d0ab0612ccff63f14742dcc
Author: FRIGN <dev@frign.de>
Date: Sun, 22 Mar 2015 22:53:12 +0100
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.
Diffstat:
M | README | | | 2 | +- |
M | xargs.1 | | | 61 | +++++++++++++++++++++++++++++++++++++++++++++++-------------- |
M | xargs.c | | | 98 | +++++++++++++++++++++++++++++++++++++++++++++---------------------------------- |
3 files changed, 104 insertions(+), 57 deletions(-)
diff --git a/README b/README
@@ -87,7 +87,7 @@ The following tools are implemented ('*' == finished, '#' == UTF-8 support,
=*| uudecode yes none
=*| uuencode yes none
#*| wc yes none
-= xargs no -I, -L, -p, -s, -t, -x
+=*| xargs no none (-p)
=*| yes non-posix none
The complement of sbase is ubase[1] which is Linux-specific and
diff --git a/xargs.1 b/xargs.1
@@ -1,15 +1,16 @@
-.Dd January 30, 2015
+.Dd March 22, 2015
.Dt XARGS 1
.Os sbase
.Sh NAME
.Nm xargs
-.Nd construct argument list(s) and execute command
+.Nd construct argument lists and execute command
.Sh SYNOPSIS
.Nm
-.Op Fl n Ar maxargs
-.Op Fl r
+.Op Fl rtx
.Op Fl E Ar eofstr
-.Op Ar cmd Op Ar arg...
+.Op Fl n Ar num
+.Op Fl s Ar num
+.Op Ar cmd Op Ar arg ...
.Sh DESCRIPTION
.Nm
reads space, tab, newline and EOF delimited strings from stdin
@@ -31,9 +32,9 @@ newlines, up to the matching double quote. Any single character, including
newlines, may be escaped by a backslash.
.Sh OPTIONS
.Bl -tag -width Ds
-.It Fl n Ar maxargs
+.It Fl n Ar num
Use at most
-.Ar maxargs
+.Ar num
arguments per command line.
.It Fl r
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
.Ar eofstr
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
.Sh EXIT STATUS
-xargs exits with one of the following values:
+.Nm
+exits with one of the following values:
.Bl -tag -width Ds
.It 0
-All invocations of command returned a zero exit status.
+All invocations of
+.Ar cmd
+returned a zero exit status.
.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
-The command exited with a 255 exit status.
+.Ar cmd
+exited with a 255 exit status.
.It 125
-The command was killed or stopped by a signal.
+.Ar cmd
+was killed or stopped by a signal.
.It 126
-The command was found but could not be executed.
+.Ar cmd
+was found but could not be executed.
.It 127
-The command could not be found.
+.Ar cmd
+could not be found.
.It 1
Some other error occurred.
.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.
diff --git a/xargs.c b/xargs.c
@@ -2,6 +2,8 @@
#include <sys/wait.h>
#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -9,12 +11,9 @@
#include "util.h"
-enum {
- NARGS = 10000
-};
+#define NARGS 10000
static int inputc(void);
-static void deinputc(int);
static void fillargbuf(int);
static int eatspace(void);
static int parsequote(int);
@@ -23,14 +22,14 @@ static char *poparg(void);
static void waitchld(void);
static void spawn(void);
-static char *cmd[NARGS];
-static char *argb;
static size_t argbsz;
static size_t argbpos;
-static long maxargs = 0;
-static int nerrors = 0;
-static char *eofstr;
-static int rflag = 0, nflag = 0;
+static size_t maxargs = 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 int
inputc(void)
@@ -39,14 +38,9 @@ inputc(void)
ch = getc(stdin);
if (ch == EOF && ferror(stdin))
- eprintf("stdin: read error:");
- return ch;
-}
+ eprintf("getc <stdin>:");
-static void
-deinputc(int ch)
-{
- ungetc(ch, stdin);
+ return ch;
}
static void
@@ -69,7 +63,7 @@ eatspace(void)
case ' ': case '\t': case '\n':
break;
default:
- deinputc(ch);
+ ungetc(ch, stdin);
return ch;
}
}
@@ -89,6 +83,7 @@ parsequote(int q)
argbpos++;
}
}
+
return -1;
}
@@ -102,6 +97,7 @@ parseescape(void)
argbpos++;
return ch;
}
+
return -1;
}
@@ -137,9 +133,8 @@ poparg(void)
}
out:
fillargbuf('\0');
- if (eofstr && strcmp(argb, eofstr) == 0)
- return NULL;
- return argb;
+
+ return (eofstr && !strcmp(argb, eofstr)) ? NULL : argb;
}
static void
@@ -154,7 +149,7 @@ waitchld(void)
if (WEXITSTATUS(status) == 127 ||
WEXITSTATUS(status) == 126)
exit(WEXITSTATUS(status));
- if (status != 0)
+ if (status)
nerrors++;
}
if (WIFSIGNALED(status))
@@ -165,6 +160,15 @@ static void
spawn(void)
{
int savederrno;
+ char **p;
+
+ if (tflag) {
+ for (p = cmd; *p; p++) {
+ fputs(*p, stderr);
+ fputc(' ', stderr);
+ }
+ fputc('\n', stderr);
+ }
switch (fork()) {
case -1:
@@ -181,26 +185,40 @@ spawn(void)
static 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
main(int argc, char *argv[])
{
int leftover = 0;
- long argsz, argmaxsz;
+ size_t argsz, argmaxsz;
char *arg = "";
int i, a;
+ argmaxsz = sysconf(_SC_ARG_MAX);
+ if (argmaxsz < 0)
+ eprintf("sysconf:");
+ /* Leave some room for environment variables */
+ argmaxsz -= 4 * 1024;
+
ARGBEGIN {
case 'n':
nflag = 1;
- if ((maxargs = strtol(EARGF(usage()), NULL, 10)) <= 0)
- eprintf("%s: value for -n option should be >= 1\n", argv0);
+ maxargs = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
break;
case 'r':
rflag = 1;
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':
eofstr = EARGF(usage());
break;
@@ -208,15 +226,9 @@ main(int argc, char *argv[])
usage();
} ARGEND;
- argmaxsz = sysconf(_SC_ARG_MAX);
- if (argmaxsz < 0)
- eprintf("sysconf:");
- /* Leave some room for environment variables */
- argmaxsz -= 4 * 1024;
-
do {
argsz = 0; i = 0; a = 0;
- if (argc > 0) {
+ if (argc) {
for (; i < argc; i++) {
cmd[i] = estrdup(argv[i]);
argsz += strlen(cmd[i]) + 1;
@@ -226,11 +238,13 @@ main(int argc, char *argv[])
argsz += strlen(cmd[i]) + 1;
i++;
}
- while (leftover == 1 || (arg = poparg())) {
- if (argsz + strlen(arg) + 1 > argmaxsz ||
- i >= NARGS - 1) {
- if (strlen(arg) + 1 > argmaxsz)
- eprintf("insufficient argument space\n");
+ while (leftover || (arg = poparg())) {
+ if (argsz + strlen(arg) + 1 > argmaxsz || i >= NARGS - 1) {
+ if (strlen(arg) + 1 > argmaxsz) {
+ weprintf("insufficient argument space\n");
+ if (xflag)
+ exit(1);
+ }
leftover = 1;
break;
}
@@ -239,13 +253,13 @@ main(int argc, char *argv[])
i++;
a++;
leftover = 0;
- if (nflag == 1 && a >= maxargs)
+ if (nflag && a >= maxargs)
break;
}
cmd[i] = NULL;
- if (a >= maxargs && nflag == 1)
+ if (a >= maxargs && nflag)
spawn();
- else if (!a || (i == 1 && rflag == 1))
+ else if (!a || (i == 1 && rflag))
;
else
spawn();
@@ -255,5 +269,5 @@ main(int argc, char *argv[])
free(argb);
- return nerrors > 0 ? 123 : 0;
+ return nerrors ? 123 : 0;
}