apply.c revision 245048
1301301Sdelphij/*-
2290001Sglebius * Copyright (c) 1994
3290001Sglebius *	The Regents of the University of California.  All rights reserved.
4301301Sdelphij *
5301301Sdelphij * This code is derived from software contributed to Berkeley by
6301301Sdelphij * Jan-Simon Pendry.
7290001Sglebius *
8290001Sglebius * Redistribution and use in source and binary forms, with or without
9290001Sglebius * modification, are permitted provided that the following conditions
10290001Sglebius * are met:
11301301Sdelphij * 1. Redistributions of source code must retain the above copyright
12290001Sglebius *    notice, this list of conditions and the following disclaimer.
13290001Sglebius * 2. Redistributions in binary form must reproduce the above copyright
14290001Sglebius *    notice, this list of conditions and the following disclaimer in the
15290001Sglebius *    documentation and/or other materials provided with the distribution.
16301301Sdelphij * 4. Neither the name of the University nor the names of its contributors
17290001Sglebius *    may be used to endorse or promote products derived from this software
18290001Sglebius *    without specific prior written permission.
19290001Sglebius *
20290001Sglebius * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21290001Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22290001Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23290001Sglebius * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24290001Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25290001Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26290001Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27290001Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28290001Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29301301Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30290001Sglebius * SUCH DAMAGE.
31290001Sglebius */
32290001Sglebius
33301301Sdelphij#if 0
34301301Sdelphij#ifndef lint
35301301Sdelphijstatic char sccsid[] = "@(#)apply.c	8.4 (Berkeley) 4/4/94";
36290001Sglebius#endif
37290001Sglebius#endif
38290001Sglebius
39290001Sglebius#include <sys/cdefs.h>
40290001Sglebius__FBSDID("$FreeBSD: head/usr.bin/apply/apply.c 245048 2013-01-04 23:44:22Z delphij $");
41290001Sglebius
42290001Sglebius#include <sys/types.h>
43301301Sdelphij#include <sys/sbuf.h>
44290001Sglebius#include <sys/wait.h>
45290001Sglebius
46301301Sdelphij#include <ctype.h>
47301301Sdelphij#include <err.h>
48301301Sdelphij#include <errno.h>
49301301Sdelphij#include <paths.h>
50301301Sdelphij#include <signal.h>
51301301Sdelphij#include <stdio.h>
52301301Sdelphij#include <stdlib.h>
53301301Sdelphij#include <string.h>
54301301Sdelphij#include <unistd.h>
55301301Sdelphij
56301301Sdelphij#define EXEC	"exec "
57301301Sdelphij
58310419Sdelphijstatic int	exec_shell(const char *, const char *, const char *);
59310419Sdelphijstatic void	usage(void);
60310419Sdelphij
61310419Sdelphijint
62310419Sdelphijmain(int argc, char *argv[])
63310419Sdelphij{
64310419Sdelphij	struct sbuf *cmdbuf;
65310419Sdelphij	long arg_max;
66310419Sdelphij	int ch, debug, i, magic, n, nargs, offset, rval;
67310419Sdelphij	size_t cmdsize;
68310419Sdelphij	char *cmd, *name, *p, *shell, *slashp, *tmpshell;
69310419Sdelphij
70310419Sdelphij	debug = 0;
71310419Sdelphij	magic = '%';		/* Default magic char is `%'. */
72310419Sdelphij	nargs = -1;
73310419Sdelphij	while ((ch = getopt(argc, argv, "a:d0123456789")) != -1)
74310419Sdelphij		switch (ch) {
75310419Sdelphij		case 'a':
76310419Sdelphij			if (optarg[1] != '\0')
77310419Sdelphij				errx(1,
78310419Sdelphij				    "illegal magic character specification");
79310419Sdelphij			magic = optarg[0];
80310419Sdelphij			break;
81310419Sdelphij		case 'd':
82310419Sdelphij			debug = 1;
83310419Sdelphij			break;
84310419Sdelphij		case '0': case '1': case '2': case '3': case '4':
85310419Sdelphij		case '5': case '6': case '7': case '8': case '9':
86310419Sdelphij			if (nargs != -1)
87310419Sdelphij				errx(1,
88310419Sdelphij				    "only one -# argument may be specified");
89310419Sdelphij			nargs = optopt - '0';
90310419Sdelphij			break;
91310419Sdelphij		default:
92310419Sdelphij			usage();
93310419Sdelphij		}
94310419Sdelphij	argc -= optind;
95310419Sdelphij	argv += optind;
96310419Sdelphij
97310419Sdelphij	if (argc < 2)
98310419Sdelphij		usage();
99310419Sdelphij
100310419Sdelphij	/*
101310419Sdelphij	 * The command to run is argv[0], and the args are argv[1..].
102310419Sdelphij	 * Look for %digit references in the command, remembering the
103310419Sdelphij	 * largest one.
104310419Sdelphij	 */
105310419Sdelphij	for (n = 0, p = argv[0]; *p != '\0'; ++p)
106310419Sdelphij		if (p[0] == magic && isdigit(p[1]) && p[1] != '0') {
107310419Sdelphij			++p;
108310419Sdelphij			if (p[0] - '0' > n)
109310419Sdelphij				n = p[0] - '0';
110310419Sdelphij		}
111310419Sdelphij
112310419Sdelphij	/*
113310419Sdelphij	 * Figure out the shell and name arguments to pass to execl()
114310419Sdelphij	 * in exec_shell().  Always malloc() shell and just set name
115310419Sdelphij	 * to point at the last part of shell if there are any backslashes,
116310419Sdelphij	 * otherwise just set it to point at the space malloc()'d.  If
117310419Sdelphij	 * SHELL environment variable exists, replace contents of
118310419Sdelphij	 * shell with it.
119310419Sdelphij	 */
120310419Sdelphij	shell = name = NULL;
121310419Sdelphij	tmpshell = getenv("SHELL");
122310419Sdelphij	shell = (tmpshell != NULL) ? strdup(tmpshell) : strdup(_PATH_BSHELL);
123310419Sdelphij	if (shell == NULL)
124310419Sdelphij		err(1, "strdup() failed");
125310419Sdelphij	slashp = strrchr(shell, '/');
126310419Sdelphij	name = (slashp != NULL) ? slashp + 1 : shell;
127310419Sdelphij
128310419Sdelphij	/*
129310419Sdelphij	 * If there were any %digit references, then use those, otherwise
130310419Sdelphij	 * build a new command string with sufficient %digit references at
131310419Sdelphij	 * the end to consume (nargs) arguments each time round the loop.
132310419Sdelphij	 * Allocate enough space to hold the maximum command.  Save the
133310419Sdelphij	 * size to pass to snprintf().
134310419Sdelphij	 */
135310419Sdelphij	cmdsize = sizeof(EXEC) - 1 + strlen(argv[0])
136310419Sdelphij	    + 9 * (sizeof(" %1") - 1) + 1;
137310419Sdelphij	if ((cmd = malloc(cmdsize)) == NULL)
138310419Sdelphij		err(1, NULL);
139310419Sdelphij
140310419Sdelphij	if (n == 0) {
141310419Sdelphij		/* If nargs not set, default to a single argument. */
142310419Sdelphij		if (nargs == -1)
143310419Sdelphij			nargs = 1;
144310419Sdelphij
145310419Sdelphij		p = cmd;
146310419Sdelphij		offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]);
147310419Sdelphij		if ((size_t)offset >= cmdsize)
148310419Sdelphij			errx(1, "snprintf() failed");
149310419Sdelphij		p += offset;
150310419Sdelphij		cmdsize -= offset;
151310419Sdelphij		for (i = 1; i <= nargs; i++) {
152310419Sdelphij			offset = snprintf(p, cmdsize, " %c%d", magic, i);
153310419Sdelphij			if ((size_t)offset >= cmdsize)
154310419Sdelphij				errx(1, "snprintf() failed");
155310419Sdelphij			p += offset;
156310419Sdelphij			cmdsize -= offset;
157310419Sdelphij		}
158310419Sdelphij
159310419Sdelphij		/*
160310419Sdelphij		 * If nargs set to the special value 0, eat a single
161310419Sdelphij		 * argument for each command execution.
162310419Sdelphij		 */
163310419Sdelphij		if (nargs == 0)
164310419Sdelphij			nargs = 1;
165310419Sdelphij	} else {
166310419Sdelphij		offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]);
167310419Sdelphij		if ((size_t)offset >= cmdsize)
168310419Sdelphij			errx(1, "snprintf() failed");
169310419Sdelphij		nargs = n;
170310419Sdelphij	}
171310419Sdelphij
172310419Sdelphij	cmdbuf = sbuf_new(NULL, NULL, 1024, SBUF_AUTOEXTEND);
173310419Sdelphij	if (cmdbuf == NULL)
174310419Sdelphij		err(1, NULL);
175310419Sdelphij
176310419Sdelphij	arg_max = sysconf(_SC_ARG_MAX);
177310419Sdelphij
178310419Sdelphij	/*
179310419Sdelphij	 * (argc) and (argv) are still offset by one to make it simpler to
180310419Sdelphij	 * expand %digit references.  At the end of the loop check for (argc)
181310419Sdelphij	 * equals 1 means that all the (argv) has been consumed.
182310419Sdelphij	 */
183310419Sdelphij	for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) {
184310419Sdelphij		sbuf_clear(cmdbuf);
185310419Sdelphij		/* Expand command argv references. */
186310419Sdelphij		for (p = cmd; *p != '\0'; ++p) {
187310419Sdelphij			if (p[0] == magic && isdigit(p[1]) && p[1] != '0') {
188310419Sdelphij				if (sbuf_cat(cmdbuf, argv[(++p)[0] - '0'])
189310419Sdelphij				    == -1)
190310419Sdelphij					errc(1, ENOMEM, "sbuf");
191310419Sdelphij			} else {
192310419Sdelphij				if (sbuf_putc(cmdbuf, *p) == -1)
193310419Sdelphij					errc(1, ENOMEM, "sbuf");
194310419Sdelphij			}
195310419Sdelphij			if (sbuf_len(cmdbuf) > arg_max)
196310419Sdelphij				errc(1, E2BIG, NULL);
197310419Sdelphij		}
198310419Sdelphij
199310419Sdelphij		/* Terminate the command string. */
200310419Sdelphij		sbuf_finish(cmdbuf);
201310419Sdelphij
202310419Sdelphij		/* Run the command. */
203310419Sdelphij		if (debug)
204310419Sdelphij			(void)printf("%s\n", sbuf_data(cmdbuf));
205310419Sdelphij		else
206310419Sdelphij			if (exec_shell(sbuf_data(cmdbuf), shell, name))
207310419Sdelphij				rval = 1;
208310419Sdelphij	}
209310419Sdelphij
210310419Sdelphij	if (argc != 1)
211310419Sdelphij		errx(1, "expecting additional argument%s after \"%s\"",
212310419Sdelphij		    (nargs - argc) ? "s" : "", argv[argc - 1]);
213310419Sdelphij	free(cmd);
214310419Sdelphij	sbuf_delete(cmdbuf);
215310419Sdelphij	free(shell);
216310419Sdelphij	exit(rval);
217310419Sdelphij}
218310419Sdelphij
219310419Sdelphij/*
220310419Sdelphij * exec_shell --
221310419Sdelphij * 	Execute a shell command using passed use_shell and use_name
222310419Sdelphij * 	arguments.
223310419Sdelphij */
224310419Sdelphijstatic int
225310419Sdelphijexec_shell(const char *command, const char *use_shell, const char *use_name)
226310419Sdelphij{
227310419Sdelphij	pid_t pid;
228310419Sdelphij	int omask, pstat;
229310419Sdelphij	sig_t intsave, quitsave;
230310419Sdelphij
231310419Sdelphij	if (!command)		/* just checking... */
232310419Sdelphij		return(1);
233310419Sdelphij
234310419Sdelphij	omask = sigblock(sigmask(SIGCHLD));
235310419Sdelphij	switch(pid = vfork()) {
236310419Sdelphij	case -1:			/* error */
237310419Sdelphij		err(1, "vfork");
238310419Sdelphij	case 0:				/* child */
239310419Sdelphij		(void)sigsetmask(omask);
240310419Sdelphij		execl(use_shell, use_name, "-c", command, (char *)NULL);
241310419Sdelphij		warn("%s", use_shell);
242310419Sdelphij		_exit(1);
243310419Sdelphij	}
244301301Sdelphij	intsave = signal(SIGINT, SIG_IGN);
245290001Sglebius	quitsave = signal(SIGQUIT, SIG_IGN);
246290001Sglebius	pid = waitpid(pid, &pstat, 0);
247290001Sglebius	(void)sigsetmask(omask);
248290001Sglebius	(void)signal(SIGINT, intsave);
249290001Sglebius	(void)signal(SIGQUIT, quitsave);
250290001Sglebius	return(pid == -1 ? -1 : pstat);
251290001Sglebius}
252290001Sglebius
253290001Sglebiusstatic void
254290001Sglebiususage(void)
255290001Sglebius{
256290001Sglebius
257310419Sdelphij	(void)fprintf(stderr,
258310419Sdelphij	"usage: apply [-a magic] [-d] [-0123456789] command arguments ...\n");
259310419Sdelphij	exit(1);
260310419Sdelphij}
261310419Sdelphij