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