/* xargs - Make and execute commands	     Author: Ian Nicholls:  1 Mar 90 */

/*
 * xargs  - Accept words from stdin until, combined with the arguments
 *	    given on the command line, just fit into the command line limit.
 *	    Then, execute the result.
 * 		e.g.    ls | xargs compress
 *			find . -name '*.s' -print | xargs ar qv libc.a
 *
 * flags: -t		Print the command just before it is run
 *	  -l len	Use len as maximum line length (default 490, max 1023)
 *	  -e ending	Append ending to the command before executing it.
 *
 * Exits with:	0  No errors.
 *		1  If any system(3) call returns a nonzero status.
 *		2  Usage error
 *		3  Line length too short to contain some single argument.
 *
 * Examples:	xargs ar qv libc.a < liborder		# Create a new libc.a
 *		find . -name '*.s' -print | xargs rm	# Remove all .s files
 *		find . -type f ! -name '*.Z' \		# Compress old files.
 *		       -atime +60 -print  | xargs compress -v
 *
 * Bugs:  If the command contains unquoted wildflags, then the system(3) call
 *		call may expand this to larger than the maximum line size.
 *	  The command is not executed if nothing was read from stdin.
 *	  xargs may give up too easily when the command returns nonzero.
 */
#define USAGE "usage: xargs [-t] [-l len] [-e endargs] command [args...]\n"

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

extern int optind;
extern char *optarg;

#ifndef MAX_ARGLINE
# define MAX_ARGLINE 1023
#endif
#ifndef min
# define min(a,b) ((a) < (b) ? (a) : (b))
#endif

char outlin[MAX_ARGLINE];
char inlin[MAX_ARGLINE];
char startlin[MAX_ARGLINE];
char *ending = NULL;
char traceflag = 0;

_PROTOTYPE(int main, (int argc, char **argv));

int main(ac,av)
int ac;
char *av[];
{
   int outlen, inlen, startlen, endlen=0, i;
   char errflg = 0;
   int maxlin = MAX_ARGLINE;

   while ((i = getopt(ac, av, "tl:e:")) != EOF)
       switch (i) {
	   case 't': traceflag = 1;	  break;
	   case 'l': maxlin = min(MAX_ARGLINE, atoi(optarg)); break;
	   case 'e': ending = optarg;	  break;
	   case '?': errflg++;		  break;
       }
   if (errflg)	{
       fprintf(stderr, USAGE);
       exit(2);
   }

   startlin[0] = 0;
   if (optind == ac) {
       strcat(startlin, "echo ");
   }
   else for ( ; optind < ac; optind++) {
       strcat(startlin, av[optind]);
       strcat(startlin, " ");
   }
   startlen = strlen(startlin);
   if (ending) endlen = strlen(ending);
   maxlin = maxlin - 1 - endlen;	/* Pre-compute */

   strcpy(outlin, startlin);
   outlen = startlen;

   while (gets(inlin) != NULL) {
       inlen = strlen(inlin);
       if (maxlin <= (outlen + inlen)) {
	   if (outlen == startlen) {
	       fprintf(stderr, "%s: Line length too short to process '%s'\n",
		       av[0], inlin);
	       exit(3);
	   }
	   if (ending) strcat(outlin, ending);
	   if (traceflag) fputs(outlin,stderr);
	   errno = 0;
	   if (0 != system(outlin)) {
	       if (errno != 0) perror("xargs");
	       exit(1);
	   }
	   strcpy(outlin, startlin);
	   outlen = startlen;
       }
       strcat(outlin, inlin);
       strcat(outlin, " ");
       outlen = outlen + inlen + 1;
   }
   if (outlen != startlen) {
       if (ending) strcat(outlin, ending);
       if (traceflag) fputs(outlin,stderr);
       errno = 0;
       if (0 != system(outlin)) {
	   if (errno != 0) perror("xargs");
	   exit(1);
       }
   }    
   return 0;
}
