sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

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:
MREADME | 2+-
Mxargs.1 | 61+++++++++++++++++++++++++++++++++++++++++++++++--------------
Mxargs.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; }