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: stable/11/usr.bin/apply/apply.c 337825 2018-08-15 01:24:43Z kevans $");
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
56337825Skevans#define ISMAGICNO(p) \
57337825Skevans	    (p)[0] == magic && isdigit((unsigned char)(p)[1]) && (p)[1] != '0'
581590Srgrimes
59245048Sdelphijstatic int	exec_shell(const char *, const char *, const char *);
6071326Swillstatic void	usage(void);
6170692Swill
621590Srgrimesint
63204761Sjhmain(int argc, char *argv[])
64204761Sjh{
65204761Sjh	struct sbuf *cmdbuf;
66204761Sjh	long arg_max;
67337825Skevans	int ch, debug, i, magic, n, nargs, rval;
68204761Sjh	size_t cmdsize;
69337825Skevans	char buf[4];
70204761Sjh	char *cmd, *name, *p, *shell, *slashp, *tmpshell;
711590Srgrimes
721590Srgrimes	debug = 0;
731590Srgrimes	magic = '%';		/* Default magic char is `%'. */
741590Srgrimes	nargs = -1;
7570672Swill	while ((ch = getopt(argc, argv, "a:d0123456789")) != -1)
761590Srgrimes		switch (ch) {
771590Srgrimes		case 'a':
78337825Skevans			if (optarg[0] == '\0' || optarg[1] != '\0')
791590Srgrimes				errx(1,
8054157Scharnier				    "illegal magic character specification");
811590Srgrimes			magic = optarg[0];
821590Srgrimes			break;
831590Srgrimes		case 'd':
841590Srgrimes			debug = 1;
851590Srgrimes			break;
861590Srgrimes		case '0': case '1': case '2': case '3': case '4':
871590Srgrimes		case '5': case '6': case '7': case '8': case '9':
881590Srgrimes			if (nargs != -1)
891590Srgrimes				errx(1,
9054157Scharnier				    "only one -# argument may be specified");
911590Srgrimes			nargs = optopt - '0';
921590Srgrimes			break;
931590Srgrimes		default:
941590Srgrimes			usage();
951590Srgrimes		}
961590Srgrimes	argc -= optind;
971590Srgrimes	argv += optind;
981590Srgrimes
991590Srgrimes	if (argc < 2)
1001590Srgrimes		usage();
1011590Srgrimes
1021590Srgrimes	/*
1031590Srgrimes	 * The command to run is argv[0], and the args are argv[1..].
1041590Srgrimes	 * Look for %digit references in the command, remembering the
1051590Srgrimes	 * largest one.
1061590Srgrimes	 */
1071590Srgrimes	for (n = 0, p = argv[0]; *p != '\0'; ++p)
108337825Skevans		if (ISMAGICNO(p)) {
1091590Srgrimes			++p;
1101590Srgrimes			if (p[0] - '0' > n)
1111590Srgrimes				n = p[0] - '0';
1121590Srgrimes		}
1131590Srgrimes
1141590Srgrimes	/*
11570692Swill	 * Figure out the shell and name arguments to pass to execl()
11670692Swill	 * in exec_shell().  Always malloc() shell and just set name
11770692Swill	 * to point at the last part of shell if there are any backslashes,
11870692Swill	 * otherwise just set it to point at the space malloc()'d.  If
11970692Swill	 * SHELL environment variable exists, replace contents of
12070692Swill	 * shell with it.
12170692Swill	 */
12270692Swill	shell = name = NULL;
12370692Swill	tmpshell = getenv("SHELL");
12470692Swill	shell = (tmpshell != NULL) ? strdup(tmpshell) : strdup(_PATH_BSHELL);
12570692Swill	if (shell == NULL)
12670692Swill		err(1, "strdup() failed");
12770692Swill	slashp = strrchr(shell, '/');
12870692Swill	name = (slashp != NULL) ? slashp + 1 : shell;
12970692Swill
13070692Swill	/*
1311590Srgrimes	 * If there were any %digit references, then use those, otherwise
1321590Srgrimes	 * build a new command string with sufficient %digit references at
1331590Srgrimes	 * the end to consume (nargs) arguments each time round the loop.
13470692Swill	 * Allocate enough space to hold the maximum command.  Save the
13570692Swill	 * size to pass to snprintf().
1361590Srgrimes	 */
137337825Skevans	if (n == 0) {
138337825Skevans		cmdsize = strlen(argv[0]) + 9 * (sizeof(" %1") - 1) + 1;
139337825Skevans		if ((cmd = malloc(cmdsize)) == NULL)
140337825Skevans			err(1, NULL);
141337825Skevans		strlcpy(cmd, argv[0], cmdsize);
1428874Srgrimes
1431590Srgrimes		/* If nargs not set, default to a single argument. */
1441590Srgrimes		if (nargs == -1)
1451590Srgrimes			nargs = 1;
1461590Srgrimes
14770692Swill		for (i = 1; i <= nargs; i++) {
148337825Skevans			snprintf(buf, sizeof(buf), " %c%d", magic, i);
149337825Skevans			strlcat(cmd, buf, cmdsize);
15070692Swill		}
1511590Srgrimes
1521590Srgrimes		/*
1531590Srgrimes		 * If nargs set to the special value 0, eat a single
1541590Srgrimes		 * argument for each command execution.
1551590Srgrimes		 */
1561590Srgrimes		if (nargs == 0)
1571590Srgrimes			nargs = 1;
1581590Srgrimes	} else {
159337825Skevans		if ((cmd = strdup(argv[0])) == NULL)
160337825Skevans			err(1, NULL);
1611590Srgrimes		nargs = n;
1621590Srgrimes	}
1631590Srgrimes
164204761Sjh	cmdbuf = sbuf_new(NULL, NULL, 1024, SBUF_AUTOEXTEND);
165204761Sjh	if (cmdbuf == NULL)
1661590Srgrimes		err(1, NULL);
1671590Srgrimes
168204761Sjh	arg_max = sysconf(_SC_ARG_MAX);
169204761Sjh
1701590Srgrimes	/*
1711590Srgrimes	 * (argc) and (argv) are still offset by one to make it simpler to
1721590Srgrimes	 * expand %digit references.  At the end of the loop check for (argc)
1731590Srgrimes	 * equals 1 means that all the (argv) has been consumed.
1741590Srgrimes	 */
1751590Srgrimes	for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) {
176204761Sjh		sbuf_clear(cmdbuf);
177337825Skevans		sbuf_cat(cmdbuf, "exec ");
1781590Srgrimes		/* Expand command argv references. */
179204761Sjh		for (p = cmd; *p != '\0'; ++p) {
180337825Skevans			if (ISMAGICNO(p)) {
181204761Sjh				if (sbuf_cat(cmdbuf, argv[(++p)[0] - '0'])
182204761Sjh				    == -1)
183204761Sjh					errc(1, ENOMEM, "sbuf");
184204761Sjh			} else {
185204761Sjh				if (sbuf_putc(cmdbuf, *p) == -1)
186204761Sjh					errc(1, ENOMEM, "sbuf");
187204761Sjh			}
188204761Sjh			if (sbuf_len(cmdbuf) > arg_max)
189204761Sjh				errc(1, E2BIG, NULL);
190204761Sjh		}
1911590Srgrimes
1921590Srgrimes		/* Terminate the command string. */
193204761Sjh		sbuf_finish(cmdbuf);
1941590Srgrimes
1951590Srgrimes		/* Run the command. */
1961590Srgrimes		if (debug)
197204761Sjh			(void)printf("%s\n", sbuf_data(cmdbuf));
1981590Srgrimes		else
199204761Sjh			if (exec_shell(sbuf_data(cmdbuf), shell, name))
2001590Srgrimes				rval = 1;
2011590Srgrimes	}
2021590Srgrimes
2031590Srgrimes	if (argc != 1)
2041590Srgrimes		errx(1, "expecting additional argument%s after \"%s\"",
205204761Sjh		    (nargs - argc) ? "s" : "", argv[argc - 1]);
20670692Swill	free(cmd);
207204761Sjh	sbuf_delete(cmdbuf);
20870692Swill	free(shell);
2091590Srgrimes	exit(rval);
2101590Srgrimes}
2111590Srgrimes
2121590Srgrimes/*
21370692Swill * exec_shell --
21470692Swill * 	Execute a shell command using passed use_shell and use_name
21570692Swill * 	arguments.
2161590Srgrimes */
21770692Swillstatic int
218245048Sdelphijexec_shell(const char *command, const char *use_shell, const char *use_name)
2191590Srgrimes{
2201590Srgrimes	pid_t pid;
22143928Seivind	int omask, pstat;
2221590Srgrimes	sig_t intsave, quitsave;
2231590Srgrimes
22470672Swill	if (!command)		/* just checking... */
2251590Srgrimes		return(1);
2261590Srgrimes
2271590Srgrimes	omask = sigblock(sigmask(SIGCHLD));
22860706Skris	switch(pid = vfork()) {
2291590Srgrimes	case -1:			/* error */
23060706Skris		err(1, "vfork");
2311590Srgrimes	case 0:				/* child */
2321590Srgrimes		(void)sigsetmask(omask);
23379452Sbrian		execl(use_shell, use_name, "-c", command, (char *)NULL);
23470692Swill		warn("%s", use_shell);
23560706Skris		_exit(1);
2361590Srgrimes	}
2371590Srgrimes	intsave = signal(SIGINT, SIG_IGN);
2381590Srgrimes	quitsave = signal(SIGQUIT, SIG_IGN);
23943928Seivind	pid = waitpid(pid, &pstat, 0);
2401590Srgrimes	(void)sigsetmask(omask);
2411590Srgrimes	(void)signal(SIGINT, intsave);
2421590Srgrimes	(void)signal(SIGQUIT, quitsave);
24343928Seivind	return(pid == -1 ? -1 : pstat);
2441590Srgrimes}
2451590Srgrimes
246245048Sdelphijstatic void
24799984Salfredusage(void)
2481590Srgrimes{
2491590Srgrimes
2501590Srgrimes	(void)fprintf(stderr,
25170672Swill	"usage: apply [-a magic] [-d] [-0123456789] command arguments ...\n");
2521590Srgrimes	exit(1);
2531590Srgrimes}
254