public domain xargs
Gordon Moffett
gam at netcom.UUCP
Thu Nov 30 19:11:54 AEST 1989
All this talk about how wonderful xargs is and how Berkeley systems don't have
it caused me to draw out this version of xargs I wrote for a V7 system some time
ago. xargs is too useful a command to live without!
The System V version of xargs uses about 500 bytes -- just 500 bytes! -- of
storage for the arg list. This version uses as much space as it can,
checking to see how big NCARGS is (on an Amdahl that was 40K!).
I wasn't sure how close to NCARGS I could go, so there is a debugging
statement left in to catch it if it does fail to exec for that reason.
Enclosed the the Makefile and the source. Comments welcome and
encouraged. Follow-ups to alt.sources.d
#!/bin/sh
# Run the following text with /bin/sh to create:
# xargs.c
# Makefile
#
sed 's/^X//' << 'SHAR_EOF' > xargs.c &&
X/* xargs.c (level 1.5 4/30/86 22:51:27) */
X/* Written by Gordon A. Moffett netcom!gam */
X/* This program is Public Domain */
X
X/* xargs -- quickie version of System V xargs(1): read a list of
X * arguments from stdin, apply to the command which is
X * the argument(s) of xargs
X */
X
X#include <stdio.h>
X#include <sys/param.h>
X
X#define EOS '\0'
X
X/*
X** exec(2) says that the arglimit is NCARGS bytes
X*/
X
X#define ARGSIZE NCARGS
X
X/*
X** since each arg is a null-terminated string, we assume at most
X** that there are ARGSIZE args, each 1 byte in size
X*/
X
X#define ARGS ARGSIZE
X
Xextern int errno;
X
Xchar *cmdname; /* should point to argv[0] */
Xchar **envir; /* 3rd arg to main() */
X
Xint cmdcnt; /* number of initial args (cmd + args) */
Xint cmdsize; /* size of initial args only */
Xint argcnt; /* number of args from stdin */
Xint argsize; /* size of stdin args PLUS initial args */
Xint envsize; /* size of environment only */
Xint totsize; /* size of environment PLUS initial args */
Xint linesize; /* size of current input line */
Xchar *argptr; /* pointer to somewhere in argbuf */
X
Xchar *args[ARGS]; /* pointers to addiontal args in argbuf */
Xchar line[BUFSIZ]; /* current input line */
Xchar argbuf[ARGSIZE]; /* command + input lines */
X
Xvoid e2big(); /* show size of args, env */
Xvoid flushargs(); /* reset arg buffer to empty */
Xint docmd(); /* do command, with this arg list */
X
Xmain(argc, argv, envp)
X int argc;
X char *argv[];
X char *envp[];
X{
X int i;
X char *gets();
X char *strcpy();
X
X cmdname = argv[0];
X envir = envp;
X
X /*
X ** skip (xargs) command name
X */
X
X argv++, argc--;
X
X /*
X ** add up the total volume of environment
X ** (we don't care what's in it, only how much there is)
X */
X
X while (*envp) {
X envsize += strlen(*envp++) + 1; /* + '\0' */
X }
X
X /*
X ** construct command from arguments
X */
X
X for (i = 0; i < argc; i++) {
X
X /*
X ** copy all arguments to the arg buffer
X */
X
X argptr = argbuf + cmdsize;
X (void) strcpy(argptr, argv[i]);
X args[cmdcnt++] = argptr;
X cmdsize += strlen(argv[i]) + 1;
X
X if (cmdcnt >= ARGS) {
X /* ?can't happen? */
X fprintf(stderr, "%s: too many initial arguments\n",
X cmdname);
X exit(1);
X }
X }
X
X /*
X ** 'totsize' is now the size of the environment + command
X */
X
X totsize = envsize + cmdsize;
X
X /*
X ** "can't happen"
X */
X
X if ( totsize > ARGSIZE) {
X fprintf(stderr, "%s: command and arguments too large (%d bytes)\n",
X cmdname, totsize);
X exit(1);
X }
X
X /*
X ** here's where all the action is: read in arguments
X ** from stdin, appending to the current arg buffer.
X ** if next line would overflow arg buffer, exec
X ** buffer and reinitialize
X */
X
X flushargs(); /* initialize arg buffers/counters */
X
X while (gets(line) != NULL) {
X
X /*
X ** note that we add 1 for the trailing '\0'
X ** thus linesize is *not* the length of the string
X ** (ie, strlen(line))
X */
X
X linesize = strlen(line) + 1 /* for '\0' */;
X
X /*
X ** if this arg would exceed the system limit,
X ** exec what we have so far
X */
X
X if ((linesize + argsize + totsize) > ARGSIZE) {
X args[argcnt++] = NULL;
X docmd(args);
X
X flushargs();
X }
X
X /*
X ** argptr points to the end of the arg buffer
X */
X
X argptr = argbuf + argsize;
X
X /*
X ** copy the arg directly into the argbuf
X ** note that we don't use strcat() --
X ** we want to retain the trailing '\0' for
X ** each string
X */
X
X (void) strcpy(argptr, line);
X
X /*
X ** record this arg
X */
X
X args[argcnt++] = argptr;
X argsize += linesize;
X }
X
X /* see if there is any left to do */
X
X if (argsize > cmdsize) {
X args[argcnt++] = NULL;
X docmd(args);
X }
X}
X
X/*
X** docmd - do the command, with the given arglist
X**
X** creates a child process to run the command,
X** waits for it, and retuns. If the child process
X** returns a non-zero exit code, an error message
X** is printed, and the program terminates with that exit code
X*/
X
Xdocmd(arglist)
Xchar *arglist[];
X{
X int w; /* return value of wait() */
X int status; /* arg to wait() */
X int pid = fork(); /* rather obvious ... */
X extern char *environ;
X
X if (pid < 0) {
X
X /*
X ** utter and complete failure
X */
X
X perror("fork");
X exit(errno);
X
X } else if (pid == 0) {
X
X /*
X ** child process - do the command
X */
X
X (void) execvp(arglist[0], arglist);
X perror(arglist[0]);
X e2big(arglist, envir, (char **) NULL);
X exit(errno);
X
X } else {
X
X /*
X ** parent process - wait for child
X */
X
X while (w != pid) {
X w = wait(&status);
X if (w < 0) {
X perror(cmdname);
X exit(errno);
X }
X }
X }
X}
X
X/*
X** flushargs - flush argument buefer
X**
X** The argument buffer is flushed so it contains only the command-string
X** the byte counters are reset accordingly
X*/
X
Xvoid
Xflushargs()
X{
X argcnt = cmdcnt;
X argsize = cmdsize;
X args[cmdsize] = EOS;
X}
X
X/*
X** e2big - show why the args and environment were too big
X**
X** show the sizes of the args, environment, and total
X*/
X
Xvoid
Xe2big(argv, envp)
Xchar *argv[];
Xchar *envp[];
X{
X int asize, esize; /* sizeof args, envirs */
X int acnt, ecnt; /* # of args, envirs */
X char **p; /* generally useful pointer */
X
X asize = 0;
X acnt = 0;
X for (p = argv; p && *p; p++) {
X asize += strlen(*p) + 1;
X acnt++;
X }
X
X fprintf(stderr, "%d argvs, %d bytes\n", acnt, asize);
X
X esize = 0;
X ecnt = 0;
X for (p = envp; p && *p; p++) {
X esize += strlen(*p) + 1;
X ecnt++;
X }
X
X fprintf(stderr, "%d envps, %d bytes\n", ecnt, esize);
X fprintf(stderr, "Total: %d args, %d bytes\n",
X ecnt+acnt, esize+asize);
X}
SHAR_EOF
chmod 0666 xargs.c || echo "restore of xargs.c fails"
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
XSHELL = /bin/sh
XI = /usr/include
XCP = cp
XDEST = /usr/local/bin
X
Xxargs: xargs.c $I/stdio.h $I/sys/param.h
X $(CC) $(CFLAGS) -o xargs xargs.c
X
Xinstall: xargs
X $(CP) xargs $(DEST)
Xclean:
Xclobber: clean
X -rm -f xargs
SHAR_EOF
chmod 0666 Makefile || echo "restore of Makefile fails"
exit 0
--
Gordon Moffett gam at netcom.UUCP
{apple,amdahl}!netcom!gam
More information about the Alt.sources
mailing list