Add initial version of xargs(1)

This commit is contained in:
sin 2014-01-03 11:52:47 +00:00
parent 71cbd12ede
commit 4bdf9a9658
3 changed files with 270 additions and 0 deletions

View File

@ -93,6 +93,7 @@ SRC = \
stat.c \
wc.c \
who.c \
xargs.c \
yes.c
OBJ = $(SRC:.c=.o) $(LIB)

28
xargs.1 Normal file
View File

@ -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.

241
xargs.c Normal file
View File

@ -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;
}