commit 4bdf9a9658c3de471418c47f55c1d4571a945ab1
parent 71cbd12ede838f8193ea5d13e3692cc57c0cf942
Author: sin <sin@2f30.org>
Date: Fri, 3 Jan 2014 11:52:47 +0000
Add initial version of xargs(1)
Diffstat:
M | Makefile | | | 1 | + |
A | xargs.1 | | | 28 | ++++++++++++++++++++++++++++ |
A | xargs.c | | | 241 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 270 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -93,6 +93,7 @@ SRC = \
stat.c \
wc.c \
who.c \
+ xargs.c \
yes.c
OBJ = $(SRC:.c=.o) $(LIB)
diff --git a/xargs.1 b/xargs.1
@@ -0,0 +1,28 @@
+.TH XARGS 1 sbase\-VERSION
+.SH NAME
+xargs \- constuct argument list(s) and execute command
+.SH SYNOPSIS
+.B xargs
+.RB [ \-r ]
+.RI [ cmd
+.RI [arg... ] ]
+.SH DESCRIPTION
+xargs reads space, tab, newline and EOF delimited strings from stdin
+and executes the specified cmd with the strings as arguments.
+
+Any arguments specified on the command line are given to the command upon
+each invocation, followed by some number of the arguments read from
+stdin. The command is repeatedly executed one or more times until stdin
+is exhausted.
+
+Spaces, tabs and newlines may be embedded in arguments using single (`'')
+or double (`"') quotes or backslashes ('\\'). Single quotes escape all
+non-single quote characters, excluding newlines, up to the matching single
+quote. Double quotes escape all non-double quote characters, excluding
+newlines, up to the matching double quote. Any single character, including
+newlines, may be escaped by a backslash.
+.SH OPTIONS
+.TP
+.BI \-r
+Do not run the command if there are no arguments. Normally the command is
+executed at least once even if there are no arguments.
diff --git a/xargs.c b/xargs.c
@@ -0,0 +1,241 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include "text.h"
+#include "util.h"
+
+enum {
+ NARGS = 5000
+};
+
+static int inputc(void);
+static void deinputc(int);
+static void fillbuf(int);
+static void eatspace(void);
+static int parsequote(int);
+static void parseescape(void);
+static char *poparg(void);
+static void pusharg(char *);
+static int runcmd(void);
+
+static char **cmd;
+static char *argb;
+static size_t argbsz = 1;
+static size_t argbpos;
+static int rflag = 0;
+
+static void
+usage(void)
+{
+ eprintf("usage: %s [-r] [cmd [arg...]]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ long argsz, argmaxsz;
+ char *arg;
+ int i;
+
+ ARGBEGIN {
+ case 'r':
+ rflag = 1;
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ argmaxsz = sysconf(_SC_ARG_MAX);
+ if (argmaxsz < 0)
+ eprintf("sysconf:");
+ /* Leave some room for environment variables */
+ argmaxsz -= 4 * 1024;
+
+ cmd = malloc(NARGS * sizeof(*cmd));
+ if (!cmd)
+ eprintf("malloc:");
+
+ argb = malloc(argbsz);
+ if (!argb)
+ eprintf("malloc:");
+
+ do {
+ argsz = 0; i = 0;
+ if (argc > 0) {
+ for (; i < argc; i++) {
+ cmd[i] = strdup(argv[i]);
+ argsz += strlen(cmd[i]) + 1;
+ }
+ } else {
+ cmd[i] = strdup("/bin/echo");
+ argsz += strlen(cmd[i]) + 1;
+ i++;
+ }
+ while ((arg = poparg())) {
+ if (argsz + strlen(arg) + 1 > argmaxsz ||
+ i >= NARGS - 1) {
+ pusharg(arg);
+ break;
+ }
+ cmd[i] = strdup(arg);
+ argsz += strlen(cmd[i]) + 1;
+ i++;
+ }
+ cmd[i] = NULL;
+ if (i == 1 && rflag == 1)
+ ;
+ else
+ if (runcmd() == -1)
+ arg = NULL;
+ for (; i >= 0; i--)
+ free(cmd[i]);
+ } while (arg);
+
+ free(argb);
+ free(cmd);
+ return 0;
+}
+
+static int
+inputc(void)
+{
+ int ch;
+
+ ch = getc(stdin);
+ if (ch == EOF && ferror(stdin))
+ eprintf("stdin: read error:");
+ return ch;
+}
+
+static void
+deinputc(int ch)
+{
+ ungetc(ch, stdin);
+}
+
+static void
+fillbuf(int ch)
+{
+ if (argbpos >= argbsz) {
+ argbsz *= 2;
+ argb = realloc(argb, argbsz);
+ if (!argb)
+ eprintf("realloc:");
+ }
+ argb[argbpos] = ch;
+}
+
+static void
+eatspace(void)
+{
+ int ch;
+
+ while ((ch = inputc()) != EOF) {
+ switch (ch) {
+ case ' ': case '\t': case '\n':
+ break;
+ default:
+ deinputc(ch);
+ return;
+ }
+ }
+}
+
+static int
+parsequote(int q)
+{
+ int ch;
+
+ while ((ch = inputc()) != EOF) {
+ if (ch == q) {
+ fillbuf('\0');
+ return 0;
+ }
+ if (ch != '\n') {
+ fillbuf(ch);
+ argbpos++;
+ }
+ }
+ return -1;
+}
+
+static void
+parseescape(void)
+{
+ int ch;
+
+ if ((ch = inputc()) != EOF) {
+ fillbuf(ch);
+ argbpos++;
+ }
+}
+
+static char *
+poparg(void)
+{
+ int ch;
+
+ argbpos = 0;
+ eatspace();
+ while ((ch = inputc()) != EOF) {
+ switch (ch) {
+ case ' ': case '\t': case '\n':
+ fillbuf('\0');
+ deinputc(ch);
+ return argb;
+ case '\'':
+ if (parsequote('\'') == -1)
+ enprintf(EXIT_FAILURE,
+ "unterminated single quote\n");
+ break;
+ case '\"':
+ if (parsequote('\"') == -1)
+ enprintf(EXIT_FAILURE,
+ "unterminated double quote\n");
+ break;
+ case '\\':
+ parseescape();
+ break;
+ default:
+ fillbuf(ch);
+ argbpos++;
+ break;
+ }
+ }
+ if (argbpos > 0) {
+ fillbuf('\0');
+ return argb;
+ }
+ return NULL;
+}
+
+static void
+pusharg(char *arg)
+{
+ char *p;
+
+ for (p = &arg[strlen(arg) - 1]; p >= arg; p--)
+ deinputc(*p);
+}
+
+static int
+runcmd(void)
+{
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0)
+ eprintf("fork:");
+ if (pid == 0) {
+ execvp(*cmd, cmd);
+ eprintf("execvp %s:", *cmd);
+ }
+ wait(&status);
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 255)
+ return -1;
+ return 0;
+}