Path: utzoo!utgpu!jarvis.csri.toronto.edu!cs.utexas.edu!samsung!rex!ames!vsi1!teraida!netcom!gam From: gam@netcom.UUCP (Gordon Moffett) Newsgroups: alt.sources Subject: public domain xargs Message-ID: <4731@netcom.UUCP> Date: 30 Nov 89 08:11:54 GMT Reply-To: gam@netcom.UUCP (Gordon Moffett) Followup-To: alt.sources.d Distribution: alt Organization: NetCom- The Bay Area's Public Access Unix System {408 997-9175 guest} Lines: 321 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 X#include 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@netcom.UUCP {apple,amdahl}!netcom!gam Brought to you by Super Global Mega Corp .com