apply.c revision 71615
1106163Sroberto/*-
2106163Sroberto * Copyright (c) 1994
3106163Sroberto *	The Regents of the University of California.  All rights reserved.
4106163Sroberto *
5106163Sroberto * This code is derived from software contributed to Berkeley by
6106163Sroberto * Jan-Simon Pendry.
7106163Sroberto *
8106163Sroberto * Redistribution and use in source and binary forms, with or without
9106163Sroberto * modification, are permitted provided that the following conditions
10106163Sroberto * are met:
11106163Sroberto * 1. Redistributions of source code must retain the above copyright
12106163Sroberto *    notice, this list of conditions and the following disclaimer.
13106163Sroberto * 2. Redistributions in binary form must reproduce the above copyright
14106163Sroberto *    notice, this list of conditions and the following disclaimer in the
15106163Sroberto *    documentation and/or other materials provided with the distribution.
16106163Sroberto * 3. All advertising materials mentioning features or use of this software
17106163Sroberto *    must display the following acknowledgement:
18106163Sroberto *	This product includes software developed by the University of
19106163Sroberto *	California, Berkeley and its contributors.
20106163Sroberto * 4. Neither the name of the University nor the names of its contributors
21106163Sroberto *    may be used to endorse or promote products derived from this software
22290000Sglebius *    without specific prior written permission.
23290000Sglebius *
24290000Sglebius * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25106163Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26106163Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27106163Sroberto * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28106163Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29106163Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30106163Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31106163Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32106163Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33106163Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34106163Sroberto * SUCH DAMAGE.
35106163Sroberto */
36106163Sroberto
37106163Sroberto#ifndef lint
38106163Sroberto#if 0
39106163Srobertostatic const char sccsid[] = "@(#)apply.c	8.4 (Berkeley) 4/4/94";
40106163Sroberto#endif
41106163Srobertostatic const char rcsid[] =
42106163Sroberto  "$FreeBSD: head/usr.bin/apply/apply.c 71615 2001-01-25 03:40:17Z will $";
43106163Sroberto#endif /* not lint */
44106163Sroberto
45106163Sroberto#include <sys/types.h>
46106163Sroberto
47106163Sroberto#include <sys/wait.h>
48106163Sroberto
49106163Sroberto#include <ctype.h>
50106163Sroberto#include <err.h>
51106163Sroberto#include <paths.h>
52106163Sroberto#include <signal.h>
53106163Sroberto#include <stdio.h>
54106163Sroberto#include <stdlib.h>
55106163Sroberto#include <string.h>
56106163Sroberto#include <unistd.h>
57106163Sroberto
58106163Sroberto#define EXEC	"exec "
59106163Sroberto
60106163Srobertostatic int	exec_shell(const char *, char *, char *);
61106163Srobertostatic void	usage(void);
62106163Sroberto
63106163Srobertoint
64106163Srobertomain(int argc, char *argv[]) {
65106163Sroberto	int ch, debug, i, magic, n, nargs, offset, rval;
66106163Sroberto	size_t clen, cmdsize, l;
67106163Sroberto	char *c, *cmd, *name, *p, *q, *shell, *slashp, *tmpshell;
68106163Sroberto
69106163Sroberto	debug = 0;
70106163Sroberto	magic = '%';		/* Default magic char is `%'. */
71106163Sroberto	nargs = -1;
72106163Sroberto	while ((ch = getopt(argc, argv, "a:d0123456789")) != -1)
73106163Sroberto		switch (ch) {
74106163Sroberto		case 'a':
75106163Sroberto			if (optarg[1] != '\0')
76106163Sroberto				errx(1,
77106163Sroberto				    "illegal magic character specification");
78106163Sroberto			magic = optarg[0];
79106163Sroberto			break;
80106163Sroberto		case 'd':
81106163Sroberto			debug = 1;
82106163Sroberto			break;
83106163Sroberto		case '0': case '1': case '2': case '3': case '4':
84106163Sroberto		case '5': case '6': case '7': case '8': case '9':
85106163Sroberto			if (nargs != -1)
86106163Sroberto				errx(1,
87106163Sroberto				    "only one -# argument may be specified");
88106163Sroberto			nargs = optopt - '0';
89106163Sroberto			break;
90106163Sroberto		default:
91106163Sroberto			usage();
92106163Sroberto		}
93106163Sroberto	argc -= optind;
94106163Sroberto	argv += optind;
95106163Sroberto
96106163Sroberto	if (argc < 2)
97106163Sroberto		usage();
98106163Sroberto
99106163Sroberto	/*
100106163Sroberto	 * The command to run is argv[0], and the args are argv[1..].
101106163Sroberto	 * Look for %digit references in the command, remembering the
102290000Sglebius	 * largest one.
103290000Sglebius	 */
104290000Sglebius	for (n = 0, p = argv[0]; *p != '\0'; ++p)
105290000Sglebius		if (p[0] == magic && isdigit(p[1]) && p[1] != '0') {
106106163Sroberto			++p;
107106163Sroberto			if (p[0] - '0' > n)
108106163Sroberto				n = p[0] - '0';
109106163Sroberto		}
110106163Sroberto
111106163Sroberto	/*
112106163Sroberto	 * Figure out the shell and name arguments to pass to execl()
113106163Sroberto	 * in exec_shell().  Always malloc() shell and just set name
114106163Sroberto	 * to point at the last part of shell if there are any backslashes,
115106163Sroberto	 * otherwise just set it to point at the space malloc()'d.  If
116106163Sroberto	 * SHELL environment variable exists, replace contents of
117106163Sroberto	 * shell with it.
118106163Sroberto	 */
119106163Sroberto	shell = name = NULL;
120106163Sroberto	tmpshell = getenv("SHELL");
121106163Sroberto	shell = (tmpshell != NULL) ? strdup(tmpshell) : strdup(_PATH_BSHELL);
122106163Sroberto	if (shell == NULL)
123106163Sroberto		err(1, "strdup() failed");
124106163Sroberto	slashp = strrchr(shell, '/');
125106163Sroberto	name = (slashp != NULL) ? slashp + 1 : shell;
126106163Sroberto
127106163Sroberto	/*
128106163Sroberto	 * If there were any %digit references, then use those, otherwise
129106163Sroberto	 * build a new command string with sufficient %digit references at
130106163Sroberto	 * the end to consume (nargs) arguments each time round the loop.
131106163Sroberto	 * Allocate enough space to hold the maximum command.  Save the
132106163Sroberto	 * size to pass to snprintf().
133106163Sroberto	 */
134106163Sroberto	cmdsize = sizeof(EXEC) - 1 + strlen(argv[0])
135106163Sroberto	    + 9 * (sizeof(" %1") - 1) + 1;
136106163Sroberto	if ((cmd = malloc(cmdsize)) == NULL)
137106163Sroberto		err(1, NULL);
138106163Sroberto
139290000Sglebius	if (n == 0) {
140290000Sglebius		/* If nargs not set, default to a single argument. */
141290000Sglebius		if (nargs == -1)
142290000Sglebius			nargs = 1;
143106163Sroberto
144106163Sroberto		p = cmd;
145106163Sroberto		offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]);
146106163Sroberto		if ((size_t)offset >= cmdsize)
147106163Sroberto			err(1, "snprintf() failed");
148106163Sroberto		p += offset;
149290000Sglebius		cmdsize -= offset;
150290000Sglebius		for (i = 1; i <= nargs; i++) {
151106163Sroberto			offset = snprintf(p, cmdsize, " %c%d", magic, i);
152106163Sroberto			if ((size_t)offset >= cmdsize)
153290000Sglebius				err(1, "snprintf() failed");
154106163Sroberto			p += offset;
155106163Sroberto			cmdsize -= offset;
156106163Sroberto		}
157290000Sglebius
158290000Sglebius		/*
159106163Sroberto		 * If nargs set to the special value 0, eat a single
160106163Sroberto		 * argument for each command execution.
161106163Sroberto		 */
162290000Sglebius		if (nargs == 0)
163106163Sroberto			nargs = 1;
164106163Sroberto	} else {
165106163Sroberto		offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]);
166106163Sroberto		if ((size_t)offset >= cmdsize)
167106163Sroberto			err(1, "snprintf() failed");
168106163Sroberto		nargs = n;
169106163Sroberto	}
170106163Sroberto
171106163Sroberto	/*
172106163Sroberto	 * Grab some space in which to build the command.  Allocate
173106163Sroberto	 * as necessary later, but no reason to build it up slowly
174106163Sroberto	 * for the normal case.
175106163Sroberto	 */
176106163Sroberto	if ((c = malloc(clen = 1024)) == NULL)
177106163Sroberto		err(1, NULL);
178106163Sroberto
179106163Sroberto	/*
180106163Sroberto	 * (argc) and (argv) are still offset by one to make it simpler to
181106163Sroberto	 * expand %digit references.  At the end of the loop check for (argc)
182106163Sroberto	 * equals 1 means that all the (argv) has been consumed.
183106163Sroberto	 */
184106163Sroberto	for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) {
185106163Sroberto		/*
186106163Sroberto		 * Find a max value for the command length, and ensure
187106163Sroberto		 * there's enough space to build it.
188106163Sroberto		 */
189106163Sroberto		for (l = strlen(cmd), i = 0; i < nargs; i++)
190290000Sglebius			l += strlen(argv[i+1]);
191290000Sglebius		if (l > clen && (c = realloc(c, clen = l)) == NULL)
192290000Sglebius			err(1, NULL);
193290000Sglebius
194290000Sglebius		/* Expand command argv references. */
195106163Sroberto		for (p = cmd, q = c; *p != '\0'; ++p)
196106163Sroberto			if (p[0] == magic && isdigit(p[1]) && p[1] != '0') {
197106163Sroberto				offset = snprintf(q, l, "%s",
198106163Sroberto				    argv[(++p)[0] - '0']);
199106163Sroberto				if ((size_t)offset >= l)
200106163Sroberto					err(1, "snprintf() failed");
201106163Sroberto				q += offset;
202106163Sroberto				l -= offset;
203106163Sroberto			} else
204106163Sroberto				*q++ = *p;
205106163Sroberto
206106163Sroberto		/* Terminate the command string. */
207106163Sroberto		*q = '\0';
208106163Sroberto
209106163Sroberto		/* Run the command. */
210106163Sroberto		if (debug)
211106163Sroberto			(void)printf("%s\n", c);
212106163Sroberto		else
213106163Sroberto			if (exec_shell(c, shell, name))
214290000Sglebius				rval = 1;
215106163Sroberto	}
216290000Sglebius
217106163Sroberto	if (argc != 1)
218106163Sroberto		errx(1, "expecting additional argument%s after \"%s\"",
219106163Sroberto	    (nargs - argc) ? "s" : "", argv[argc - 1]);
220106163Sroberto	free(cmd);
221106163Sroberto	free(c);
222106163Sroberto	free(shell);
223106163Sroberto	exit(rval);
224106163Sroberto}
225106163Sroberto
226106163Sroberto/*
227106163Sroberto * exec_shell --
228106424Sroberto * 	Execute a shell command using passed use_shell and use_name
229106163Sroberto * 	arguments.
230106163Sroberto */
231106163Srobertostatic int
232106163Srobertoexec_shell(const char *command, char *use_shell, char *use_name)
233106163Sroberto{
234106163Sroberto	pid_t pid;
235106163Sroberto	int omask, pstat;
236106163Sroberto	sig_t intsave, quitsave;
237106163Sroberto
238106163Sroberto	if (!command)		/* just checking... */
239106163Sroberto		return(1);
240106163Sroberto
241106163Sroberto	omask = sigblock(sigmask(SIGCHLD));
242106163Sroberto	switch(pid = vfork()) {
243106163Sroberto	case -1:			/* error */
244106163Sroberto		err(1, "vfork");
245106163Sroberto	case 0:				/* child */
246106163Sroberto		(void)sigsetmask(omask);
247106163Sroberto		execl(use_shell, use_name, "-c", command, NULL);
248106163Sroberto		warn("%s", use_shell);
249106163Sroberto		_exit(1);
250106163Sroberto	}
251106163Sroberto	intsave = signal(SIGINT, SIG_IGN);
252106163Sroberto	quitsave = signal(SIGQUIT, SIG_IGN);
253106163Sroberto	pid = waitpid(pid, &pstat, 0);
254106163Sroberto	(void)sigsetmask(omask);
255106163Sroberto	(void)signal(SIGINT, intsave);
256106163Sroberto	(void)signal(SIGQUIT, quitsave);
257106163Sroberto	return(pid == -1 ? -1 : pstat);
258106163Sroberto}
259106163Sroberto
260106163Srobertovoid
261106163Srobertousage()
262106163Sroberto{
263106163Sroberto
264106163Sroberto	(void)fprintf(stderr,
265106163Sroberto	"usage: apply [-a magic] [-d] [-0123456789] command arguments ...\n");
266106163Sroberto	exit(1);
267106163Sroberto}
268106163Sroberto