11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1994
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * This code is derived from software contributed to Berkeley by
61590Srgrimes * Jan-Simon Pendry.
71590Srgrimes *
81590Srgrimes * Redistribution and use in source and binary forms, with or without
91590Srgrimes * modification, are permitted provided that the following conditions
101590Srgrimes * are met:
111590Srgrimes * 1. Redistributions of source code must retain the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer.
131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer in the
151590Srgrimes *    documentation and/or other materials provided with the distribution.
161590Srgrimes * 4. Neither the name of the University nor the names of its contributors
171590Srgrimes *    may be used to endorse or promote products derived from this software
181590Srgrimes *    without specific prior written permission.
191590Srgrimes *
201590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301590Srgrimes * SUCH DAMAGE.
311590Srgrimes */
321590Srgrimes
3387628Sdwmalone#if 0
3487628Sdwmalone#ifndef lint
3587628Sdwmalonestatic char sccsid[] = "@(#)apply.c	8.4 (Berkeley) 4/4/94";
3687628Sdwmalone#endif
3787628Sdwmalone#endif
3887628Sdwmalone
3987231Smarkm#include <sys/cdefs.h>
4087231Smarkm__FBSDID("$FreeBSD$");
4187231Smarkm
4267189Sbrian#include <sys/types.h>
43204761Sjh#include <sys/sbuf.h>
441590Srgrimes#include <sys/wait.h>
451590Srgrimes
461590Srgrimes#include <ctype.h>
471590Srgrimes#include <err.h>
48204761Sjh#include <errno.h>
491590Srgrimes#include <paths.h>
501590Srgrimes#include <signal.h>
511590Srgrimes#include <stdio.h>
521590Srgrimes#include <stdlib.h>
531590Srgrimes#include <string.h>
541590Srgrimes#include <unistd.h>
551590Srgrimes
5671326Swill#define EXEC	"exec "
571590Srgrimes
58245048Sdelphijstatic int	exec_shell(const char *, const char *, const char *);
5971326Swillstatic void	usage(void);
6070692Swill
611590Srgrimesint
62204761Sjhmain(int argc, char *argv[])
63204761Sjh{
64204761Sjh	struct sbuf *cmdbuf;
65204761Sjh	long arg_max;
6671326Swill	int ch, debug, i, magic, n, nargs, offset, rval;
67204761Sjh	size_t cmdsize;
68204761Sjh	char *cmd, *name, *p, *shell, *slashp, *tmpshell;
691590Srgrimes
701590Srgrimes	debug = 0;
711590Srgrimes	magic = '%';		/* Default magic char is `%'. */
721590Srgrimes	nargs = -1;
7370672Swill	while ((ch = getopt(argc, argv, "a:d0123456789")) != -1)
741590Srgrimes		switch (ch) {
751590Srgrimes		case 'a':
761590Srgrimes			if (optarg[1] != '\0')
771590Srgrimes				errx(1,
7854157Scharnier				    "illegal magic character specification");
791590Srgrimes			magic = optarg[0];
801590Srgrimes			break;
811590Srgrimes		case 'd':
821590Srgrimes			debug = 1;
831590Srgrimes			break;
841590Srgrimes		case '0': case '1': case '2': case '3': case '4':
851590Srgrimes		case '5': case '6': case '7': case '8': case '9':
861590Srgrimes			if (nargs != -1)
871590Srgrimes				errx(1,
8854157Scharnier				    "only one -# argument may be specified");
891590Srgrimes			nargs = optopt - '0';
901590Srgrimes			break;
911590Srgrimes		default:
921590Srgrimes			usage();
931590Srgrimes		}
941590Srgrimes	argc -= optind;
951590Srgrimes	argv += optind;
961590Srgrimes
971590Srgrimes	if (argc < 2)
981590Srgrimes		usage();
991590Srgrimes
1001590Srgrimes	/*
1011590Srgrimes	 * The command to run is argv[0], and the args are argv[1..].
1021590Srgrimes	 * Look for %digit references in the command, remembering the
1031590Srgrimes	 * largest one.
1041590Srgrimes	 */
1051590Srgrimes	for (n = 0, p = argv[0]; *p != '\0'; ++p)
1061590Srgrimes		if (p[0] == magic && isdigit(p[1]) && p[1] != '0') {
1071590Srgrimes			++p;
1081590Srgrimes			if (p[0] - '0' > n)
1091590Srgrimes				n = p[0] - '0';
1101590Srgrimes		}
1111590Srgrimes
1121590Srgrimes	/*
11370692Swill	 * Figure out the shell and name arguments to pass to execl()
11470692Swill	 * in exec_shell().  Always malloc() shell and just set name
11570692Swill	 * to point at the last part of shell if there are any backslashes,
11670692Swill	 * otherwise just set it to point at the space malloc()'d.  If
11770692Swill	 * SHELL environment variable exists, replace contents of
11870692Swill	 * shell with it.
11970692Swill	 */
12070692Swill	shell = name = NULL;
12170692Swill	tmpshell = getenv("SHELL");
12270692Swill	shell = (tmpshell != NULL) ? strdup(tmpshell) : strdup(_PATH_BSHELL);
12370692Swill	if (shell == NULL)
12470692Swill		err(1, "strdup() failed");
12570692Swill	slashp = strrchr(shell, '/');
12670692Swill	name = (slashp != NULL) ? slashp + 1 : shell;
12770692Swill
12870692Swill	/*
1291590Srgrimes	 * If there were any %digit references, then use those, otherwise
1301590Srgrimes	 * build a new command string with sufficient %digit references at
1311590Srgrimes	 * the end to consume (nargs) arguments each time round the loop.
13270692Swill	 * Allocate enough space to hold the maximum command.  Save the
13370692Swill	 * size to pass to snprintf().
1341590Srgrimes	 */
13571326Swill	cmdsize = sizeof(EXEC) - 1 + strlen(argv[0])
13671326Swill	    + 9 * (sizeof(" %1") - 1) + 1;
13770692Swill	if ((cmd = malloc(cmdsize)) == NULL)
1381590Srgrimes		err(1, NULL);
1398874Srgrimes
1401590Srgrimes	if (n == 0) {
1411590Srgrimes		/* If nargs not set, default to a single argument. */
1421590Srgrimes		if (nargs == -1)
1431590Srgrimes			nargs = 1;
1441590Srgrimes
1451590Srgrimes		p = cmd;
14670692Swill		offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]);
14782057Sbrian		if ((size_t)offset >= cmdsize)
148204761Sjh			errx(1, "snprintf() failed");
14970692Swill		p += offset;
15071615Swill		cmdsize -= offset;
15170692Swill		for (i = 1; i <= nargs; i++) {
15270692Swill			offset = snprintf(p, cmdsize, " %c%d", magic, i);
15382057Sbrian			if ((size_t)offset >= cmdsize)
154204761Sjh				errx(1, "snprintf() failed");
15570692Swill			p += offset;
15671615Swill			cmdsize -= offset;
15770692Swill		}
1581590Srgrimes
1591590Srgrimes		/*
1601590Srgrimes		 * If nargs set to the special value 0, eat a single
1611590Srgrimes		 * argument for each command execution.
1621590Srgrimes		 */
1631590Srgrimes		if (nargs == 0)
1641590Srgrimes			nargs = 1;
1651590Srgrimes	} else {
16670692Swill		offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]);
16782057Sbrian		if ((size_t)offset >= cmdsize)
168204761Sjh			errx(1, "snprintf() failed");
1691590Srgrimes		nargs = n;
1701590Srgrimes	}
1711590Srgrimes
172204761Sjh	cmdbuf = sbuf_new(NULL, NULL, 1024, SBUF_AUTOEXTEND);
173204761Sjh	if (cmdbuf == NULL)
1741590Srgrimes		err(1, NULL);
1751590Srgrimes
176204761Sjh	arg_max = sysconf(_SC_ARG_MAX);
177204761Sjh
1781590Srgrimes	/*
1791590Srgrimes	 * (argc) and (argv) are still offset by one to make it simpler to
1801590Srgrimes	 * expand %digit references.  At the end of the loop check for (argc)
1811590Srgrimes	 * equals 1 means that all the (argv) has been consumed.
1821590Srgrimes	 */
1831590Srgrimes	for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) {
184204761Sjh		sbuf_clear(cmdbuf);
1851590Srgrimes		/* Expand command argv references. */
186204761Sjh		for (p = cmd; *p != '\0'; ++p) {
18770692Swill			if (p[0] == magic && isdigit(p[1]) && p[1] != '0') {
188204761Sjh				if (sbuf_cat(cmdbuf, argv[(++p)[0] - '0'])
189204761Sjh				    == -1)
190204761Sjh					errc(1, ENOMEM, "sbuf");
191204761Sjh			} else {
192204761Sjh				if (sbuf_putc(cmdbuf, *p) == -1)
193204761Sjh					errc(1, ENOMEM, "sbuf");
194204761Sjh			}
195204761Sjh			if (sbuf_len(cmdbuf) > arg_max)
196204761Sjh				errc(1, E2BIG, NULL);
197204761Sjh		}
1981590Srgrimes
1991590Srgrimes		/* Terminate the command string. */
200204761Sjh		sbuf_finish(cmdbuf);
2011590Srgrimes
2021590Srgrimes		/* Run the command. */
2031590Srgrimes		if (debug)
204204761Sjh			(void)printf("%s\n", sbuf_data(cmdbuf));
2051590Srgrimes		else
206204761Sjh			if (exec_shell(sbuf_data(cmdbuf), shell, name))
2071590Srgrimes				rval = 1;
2081590Srgrimes	}
2091590Srgrimes
2101590Srgrimes	if (argc != 1)
2111590Srgrimes		errx(1, "expecting additional argument%s after \"%s\"",
212204761Sjh		    (nargs - argc) ? "s" : "", argv[argc - 1]);
21370692Swill	free(cmd);
214204761Sjh	sbuf_delete(cmdbuf);
21570692Swill	free(shell);
2161590Srgrimes	exit(rval);
2171590Srgrimes}
2181590Srgrimes
2191590Srgrimes/*
22070692Swill * exec_shell --
22170692Swill * 	Execute a shell command using passed use_shell and use_name
22270692Swill * 	arguments.
2231590Srgrimes */
22470692Swillstatic int
225245048Sdelphijexec_shell(const char *command, const char *use_shell, const char *use_name)
2261590Srgrimes{
2271590Srgrimes	pid_t pid;
22843928Seivind	int omask, pstat;
2291590Srgrimes	sig_t intsave, quitsave;
2301590Srgrimes
23170672Swill	if (!command)		/* just checking... */
2321590Srgrimes		return(1);
2331590Srgrimes
2341590Srgrimes	omask = sigblock(sigmask(SIGCHLD));
23560706Skris	switch(pid = vfork()) {
2361590Srgrimes	case -1:			/* error */
23760706Skris		err(1, "vfork");
2381590Srgrimes	case 0:				/* child */
2391590Srgrimes		(void)sigsetmask(omask);
24079452Sbrian		execl(use_shell, use_name, "-c", command, (char *)NULL);
24170692Swill		warn("%s", use_shell);
24260706Skris		_exit(1);
2431590Srgrimes	}
2441590Srgrimes	intsave = signal(SIGINT, SIG_IGN);
2451590Srgrimes	quitsave = signal(SIGQUIT, SIG_IGN);
24643928Seivind	pid = waitpid(pid, &pstat, 0);
2471590Srgrimes	(void)sigsetmask(omask);
2481590Srgrimes	(void)signal(SIGINT, intsave);
2491590Srgrimes	(void)signal(SIGQUIT, quitsave);
25043928Seivind	return(pid == -1 ? -1 : pstat);
2511590Srgrimes}
2521590Srgrimes
253245048Sdelphijstatic void
25499984Salfredusage(void)
2551590Srgrimes{
2561590Srgrimes
2571590Srgrimes	(void)fprintf(stderr,
25870672Swill	"usage: apply [-a magic] [-d] [-0123456789] command arguments ...\n");
2591590Srgrimes	exit(1);
2601590Srgrimes}
261