gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (10/20/85)
#!/bin/sh # Self-unpacking archive format. To unbundle, sh this file. echo 'apply.1' 1>&2 cat >'apply.1' <<'END OF apply.1' .TH APPLY 1V VMB '\" last edit: 85/10/20 D A Gwyn '\" SCCS ID: @(#)apply.1 1.1 .SH NAME apply \- apply a command to a set of arguments .SH SYNOPSIS .B apply [ .BR \-a c ] [ \-n ] [ .B \-v ] [ .B \-w ] command args ... .SH DESCRIPTION .I Apply\^ runs the named .I command\^ on successive batches of arguments taken from .I args\^ until they are all consumed. The optional number .I n\^ specifies the number of arguments per batch (default 1); this many arguments (separated by spaces) are appended to .I command\^ each time it is run. In the special case that .I n\^ is zero, .I command\^ is run without arguments once for each .IR arg\^ . .P However, if .I command\^ contains any character sequence consisting of the character .I c\^ (default .BR % ) followed by a digit .I d\^ from 1 through 9, .I n\^ is ignored and each such .I cd\^ pair is replaced by the .IR d\^ th following unconsumed .IR arg\^ . In this case, the maximum such digit .I d\^ in .I command\^ specifies the number of .I args\^ consumed per batch. .P The .B \-v (``verbose'') option causes .I apply\^ to print each constructed command before it is run. The .B \-w option causes a warning to be printed for any command that returns a non-zero exit status. .SH CAVEATS The constructed command is executed by .IR system\^ (3S), so watch out for shell metacharacters in .I command\^ or .IR args\^ . It is best to enclose complicated commands in single quotes \'\ \', to prevent immediate interpretation by the current shell process. .P It is necessary to choose an alternate substitution prefix character .IR c\^ , if the default .B % character must appear literally followed by a positive digit in the constructed command. .SH EXAMPLES The following shell command inefficiently emulates the behavior of `ls': .RS $ \|\fIapply \|echo \|*\fP .RE .P The following shell command compares the `a' files against the corresponding `b' files: .RS $ \|\fIapply \|\-2 \|cmp \|a1 \|b1 \|a2 \|b2 \|...\fP .RE .P The following shell command runs .IR date\^ (1) 20 times; like `for i in \`seq 20\`; do date; done': .RS $ \|\fIapply \|\-0 \|date \|\`seq \|20\`\fP .RE .P The following shell command exchanges the names of the `a' files with the corresponding `b' files: .RS $ \|\fIapply \|\'mv \|%1 \|t; mv \|%2 \|%1; mv \|t \|%2\' \|a1 \|b1 \|a2 \|b2 \|...\fP .RE .SH "SEE ALSO" find(1), pick(1V), sh(1). .SH DIAGNOSTICS Error messages, as well as requested verbose or warning messages, are printed on the standard error output and are meant to be self-explanatory. ``Command too long'' indicates that the result of adding arguments to the .I command\^ would exceed some system limit on the total length of a command line. .RI `` N\^ args left over'' indicates that the number of arguments per batch did not exactly divide the number of supplied .IR args\^ . .SH "EXIT CODES" If all applications of .I command\^ return zero exit status, then so will .IR apply\^ . An exit status of 1 is returned if any .I command\^ execution errors occur; 2 is returned for usage errors. .SH AUTHOR Douglas A. Gwyn, BRL/VLD-VMB (inspired by 8th Edition UNIX) END OF apply.1 echo 'apply.c' 1>&2 cat >'apply.c' <<'END OF apply.c' /* apply -- apply a command to a set of arguments last edit: 85/10/20 D A Gwyn Usage: $ apply[ -a<c>][ -<n>][ -v][ -w] <command> <args ...> Runs the named <command> repeatedly, <n> (default 1) successive arguments from <args ...> passed to <command> each time. Special case: If <n> is 0, runs <command> as if <n> were 1, but does not pass any arguments to the <command>. Special kludge: If <command> contains <c> (default %) followed by a digit from 1 through 9, <n> is ignored and the <c><digit> pairs are replaced by the corresponding remaining unused <args>, consuming as many <args> as the largest <digit> in <command>. Added by DAG: The -v (verbose) option prints each constructed command before executing it. If -w is specified, commands that return nonzero exit status will cause a warning message. Credit: This is a public domain implementation of the 8th Edition UNIX command of the same name, working entirely from the manual spec. */ #ifndef lint static char sccsid[] = "@(#)apply.c 1.1"; #endif #include <ctype.h> #include <stdio.h> #include <string.h> extern void exit(); extern int atoi(); #define MAX_CMD_LEN BUFSIZ /* allowed command size */ static int verbose = 0; /* set for command printout */ static int warn = 0; /* set for failure warnings */ static char c = '%'; /* magic substitution prefix */ static int n = 1; /* real arguments per command */ static int inc = 1; /* args consumed per command */ static char *command; /* command template */ static char buf[MAX_CMD_LEN+1]; /* shell command built here */ static int errors = 0; /* 1 if any execution errors */ #define ToDigit( c ) ((c) - '0') /* convert char to int digit */ static void LeftOver(), PutBuf(), Usage(); main( argc, argv ) int argc; char *argv[]; { /* process options */ while ( --argc > 0 && (*++argv)[0] == '-' ) if ( (*argv)[1] == 'v' && (*argv)[2] == '\0' ) verbose = 1; /* "-v" */ else if ( (*argv)[1] == 'w' && (*argv)[2] == '\0' ) warn = 1; /* "-w" */ else if ( (*argv)[1] == 'a' ) /* "-a<c>" */ { /* wish we could use getopt() here */ if ( (c = (*argv)[2]) != '\0' && (*argv)[3] != '\0' || (c == '\0' && (--argc <= 0 || (c = (*++argv)[0]) == '\0' || (*argv)[1] != '\0' ) ) ) { (void)fprintf( stderr, "apply: bad -a value\n" ); Usage(); } } else if ( isdigit( (*argv)[1] ) ) /* "-<n>" */ { if ( (n = atoi( &(*argv)[1] )) > 0 ) inc = n; /* else inc = 1; */ } else { (void)fprintf( stderr, "apply: unknown option \"%s\"\n", *argv ); Usage(); } /* save command template */ if ( --argc < 0 ) { (void)fprintf( stderr, "apply: missing command\n" ); Usage(); } command = *argv++; /* execute constructed command repeatedly */ while ( argc > 0 ) /* unconsumed <args> remain */ { register char *cp; /* -> <command> template */ register char *bp; /* -> construction buffer */ register int max = 0; /* maximum <digit> */ /* copy <command> template into construction buffer, looking for and performing <c><digit> substitution */ for ( bp = buf, cp = command; *cp != '\0'; ++cp ) if ( *cp == c /* <c> matched, try <digit> */ && isdigit( cp[1] ) && cp[1] != '0' ) { register char *ap; /* -> arg[.] */ register int digit; /* arg # */ ++cp; /* -> <digit> */ if ( (digit = ToDigit( *cp )) > max ) max = digit; if ( digit > argc ) LeftOver( argc ); for ( ap = argv[digit - 1]; *ap != '\0'; ++ap ) PutBuf( bp++, *ap ); } else PutBuf( bp++, *cp ); if ( max > 0 ) /* substitution performed */ { argc -= max; argv += max; } else { register char *ap; /* -> arg[.] */ register int i; /* arg # - 1 */ if ( inc > argc ) LeftOver( argc ); /* append <n> arguments separated by spaces */ for ( i = 0; i < n; ++i ) { PutBuf( bp++, ' ' ); for ( ap = argv[i]; *ap != '\0'; ++ap ) PutBuf( bp++, *ap ); } argc -= inc; argv += inc; } PutBuf( bp, '\0' ); /* terminate string */ /* execute constructed command */ if ( verbose ) (void)fprintf( stderr, "apply: %s\n", buf ); if ( system( buf ) != 0 ) { errors = 1; if ( warn ) (void)fprintf( stderr, "apply: \"%s\" failed\n", buf ); } } return errors; } static void PutBuf( bp, ch ) /* store character in buf[] */ register char *bp; /* -> buf[.] */ int ch; /* character to be stored */ { if ( bp > &buf[MAX_CMD_LEN] ) { (void)fprintf( stderr, "apply: command too long\n" ); exit( 1 ); } *bp = ch; } static void LeftOver( argc ) /* warn about LeftOver args */ int argc; /* how many left over */ { (void)fprintf( stderr, "apply: %d arg%s left over\n", argc, argc == 1 ? "" : "s" ); exit( 1 ); } static void Usage() /* print usage message */ { (void)fprintf( stderr, "Usage:\tapply [-a<c>] [-<n>] [-v] [-w] <command> <args ...>\n" ); exit( 2 ); } END OF apply.c echo 'apply.mk' 1>&2 cat >'apply.mk' <<'END OF apply.mk' # apply.mk -- makefile for "apply" utility # last edit: 85/10/20 D A Gwyn # SCCS ID: @(#)apply.mk 1.1 PRODUCT = apply MKFILE = ${PRODUCT}.mk CFILES = ${PRODUCT}.c OBJS = ${PRODUCT}.o LDFLAGS = -n BINDIR = /vld/bin MANDIR = /usr/5lib/man/local/man1 BINPERM = 755 MANPERM = 664 TESTDIR = . INS = cp .DEFAULT: $(GET) $(GFLAGS) -p s.$@ > ${TESTDIR}/$@ touch $@ all: ${PRODUCT} ${PRODUCT}.1 ${PRODUCT}: ${OBJS} $(CC) -o ${TESTDIR}/$@ ${LDFLAGS} ${OBJS} size ${TESTDIR}/$@ touch $@ print: ${PRODUCT}.1 ${MKFILE} ${CFILES} ( nroff -Tlp -man ${TESTDIR}/${PRODUCT}.1 ; \ pr ${MKFILE} ${CFILES} ${TESTDIR}/${PRODUCT}.1 ) | lpr lint: ${CFILES} lint ${CFILES} > ${PRODUCT}.lint compare: all cmp ${BINDIR}/${PRODUCT} ${TESTDIR}/${PRODUCT} cmp ${MANDIR}/${PRODUCT}.1 ${TESTDIR}/${PRODUCT}.1 install: all ${INS} ${TESTDIR}/${PRODUCT} ${BINDIR} chmod ${BINPERM} ${BINDIR}/${PRODUCT} ${INS} ${TESTDIR}/${PRODUCT}.1 ${MANDIR} chmod ${MANPERM} ${MANDIR}/${PRODUCT}.1 clean: -if vax; then rm -f ${CFILES}; fi -rm -f ${OBJS} ${PRODUCT}.lint clobber: clean -if vax; then rm -f ${TESTDIR}/${PRODUCT}.1; fi -rm -f ${TESTDIR}/${PRODUCT} END OF apply.mk