apply.c revision 60706
1/*-
2 * Copyright (c) 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Jan-Simon Pendry.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38#if 0
39static const char sccsid[] = "@(#)apply.c	8.4 (Berkeley) 4/4/94";
40#endif
41static const char rcsid[] =
42  "$FreeBSD: head/usr.bin/apply/apply.c 60706 2000-05-19 09:42:53Z kris $";
43#endif /* not lint */
44
45#include <sys/wait.h>
46
47#include <ctype.h>
48#include <err.h>
49#include <paths.h>
50#include <signal.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <unistd.h>
55
56void	usage __P((void));
57int	system __P((const char *));
58
59int
60main(argc, argv)
61	int argc;
62	char *argv[];
63{
64	int ch, clen, debug, i, l, magic, n, nargs, rval;
65	char *c, *cmd, *p, *q;
66
67	debug = 0;
68	magic = '%';		/* Default magic char is `%'. */
69	nargs = -1;
70	while ((ch = getopt(argc, argv, "a:d0123456789")) != -1)
71		switch (ch) {
72		case 'a':
73			if (optarg[1] != '\0')
74				errx(1,
75				    "illegal magic character specification");
76			magic = optarg[0];
77			break;
78		case 'd':
79			debug = 1;
80			break;
81		case '0': case '1': case '2': case '3': case '4':
82		case '5': case '6': case '7': case '8': case '9':
83			if (nargs != -1)
84				errx(1,
85				    "only one -# argument may be specified");
86			nargs = optopt - '0';
87			break;
88		default:
89			usage();
90		}
91	argc -= optind;
92	argv += optind;
93
94	if (argc < 2)
95		usage();
96
97	/*
98	 * The command to run is argv[0], and the args are argv[1..].
99	 * Look for %digit references in the command, remembering the
100	 * largest one.
101	 */
102	for (n = 0, p = argv[0]; *p != '\0'; ++p)
103		if (p[0] == magic && isdigit(p[1]) && p[1] != '0') {
104			++p;
105			if (p[0] - '0' > n)
106				n = p[0] - '0';
107		}
108
109	/*
110	 * If there were any %digit references, then use those, otherwise
111	 * build a new command string with sufficient %digit references at
112	 * the end to consume (nargs) arguments each time round the loop.
113	 * Allocate enough space to hold the maximum command.
114	 */
115	if ((cmd = malloc(sizeof("exec ") - 1 +
116	    strlen(argv[0]) + 9 * (sizeof(" %1") - 1) + 1)) == NULL)
117		err(1, NULL);
118
119	if (n == 0) {
120		/* If nargs not set, default to a single argument. */
121		if (nargs == -1)
122			nargs = 1;
123
124		p = cmd;
125		p += sprintf(cmd, "exec %s", argv[0]);
126		for (i = 1; i <= nargs; i++)
127			p += sprintf(p, " %c%d", magic, i);
128
129		/*
130		 * If nargs set to the special value 0, eat a single
131		 * argument for each command execution.
132		 */
133		if (nargs == 0)
134			nargs = 1;
135	} else {
136		(void)sprintf(cmd, "exec %s", argv[0]);
137		nargs = n;
138	}
139
140	/*
141	 * Grab some space in which to build the command.  Allocate
142	 * as necessary later, but no reason to build it up slowly
143	 * for the normal case.
144	 */
145	if ((c = malloc(clen = 1024)) == NULL)
146		err(1, NULL);
147
148	/*
149	 * (argc) and (argv) are still offset by one to make it simpler to
150	 * expand %digit references.  At the end of the loop check for (argc)
151	 * equals 1 means that all the (argv) has been consumed.
152	 */
153	for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) {
154		/*
155		 * Find a max value for the command length, and ensure
156		 * there's enough space to build it.
157		 */
158		for (l = strlen(cmd), i = 0; i < nargs; i++)
159			l += strlen(argv[i+1]);
160		if (l > clen && (c = realloc(c, clen = l)) == NULL)
161			err(1, NULL);
162
163		/* Expand command argv references. */
164		for (p = cmd, q = c; *p != '\0'; ++p)
165			if (p[0] == magic && isdigit(p[1]) && p[1] != '0')
166				q += sprintf(q, "%s", argv[(++p)[0] - '0']);
167			else
168				*q++ = *p;
169
170		/* Terminate the command string. */
171		*q = '\0';
172
173		/* Run the command. */
174		if (debug)
175			(void)printf("%s\n", c);
176		else
177			if (system(c))
178				rval = 1;
179	}
180
181	if (argc != 1)
182		errx(1, "expecting additional argument%s after \"%s\"",
183		    (nargs - argc) ? "s" : "", argv[argc - 1]);
184	exit(rval);
185}
186
187/*
188 * system --
189 * 	Private version of system(3).  Use the user's SHELL environment
190 *	variable as the shell to execute.
191 */
192int
193system(command)
194	const char *command;
195{
196	static char *name, *shell;
197	pid_t pid;
198	int omask, pstat;
199	sig_t intsave, quitsave;
200
201	if (shell == NULL) {
202		if ((shell = getenv("SHELL")) == NULL)
203			shell = _PATH_BSHELL;
204		if ((name = strrchr(shell, '/')) == NULL)
205			name = shell;
206		else
207			++name;
208	}
209	if (!command)		/* just checking... */
210		return(1);
211
212	omask = sigblock(sigmask(SIGCHLD));
213	switch(pid = vfork()) {
214	case -1:			/* error */
215		err(1, "vfork");
216	case 0:				/* child */
217		(void)sigsetmask(omask);
218		execl(shell, name, "-c", command, NULL);
219		warn("%s", shell);
220		_exit(1);
221	}
222	intsave = signal(SIGINT, SIG_IGN);
223	quitsave = signal(SIGQUIT, SIG_IGN);
224	pid = waitpid(pid, &pstat, 0);
225	(void)sigsetmask(omask);
226	(void)signal(SIGINT, intsave);
227	(void)signal(SIGQUIT, quitsave);
228	return(pid == -1 ? -1 : pstat);
229}
230
231void
232usage()
233{
234
235	(void)fprintf(stderr,
236	"usage: apply [-a magic] [-d] [-0123456789] command arguments ...\n");
237	exit(1);
238}
239