apply.c revision 82057
115177Snate/*-
215284Snate * Copyright (c) 1994
315284Snate *	The Regents of the University of California.  All rights reserved.
415284Snate *
515284Snate * This code is derived from software contributed to Berkeley by
615284Snate * Jan-Simon Pendry.
715284Snate *
815284Snate * Redistribution and use in source and binary forms, with or without
915284Snate * modification, are permitted provided that the following conditions
1015284Snate * are met:
1115284Snate * 1. Redistributions of source code must retain the above copyright
1215284Snate *    notice, this list of conditions and the following disclaimer.
1315284Snate * 2. Redistributions in binary form must reproduce the above copyright
1415284Snate *    notice, this list of conditions and the following disclaimer in the
1515284Snate *    documentation and/or other materials provided with the distribution.
1615284Snate * 3. All advertising materials mentioning features or use of this software
1715284Snate *    must display the following acknowledgement:
1815284Snate *	This product includes software developed by the University of
1915284Snate *	California, Berkeley and its contributors.
2015284Snate * 4. Neither the name of the University nor the names of its contributors
2115284Snate *    may be used to endorse or promote products derived from this software
2215284Snate *    without specific prior written permission.
2315284Snate *
2415284Snate * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2510217Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2630171Scharnier * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2730171Scharnier * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2830171Scharnier * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2950479Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3030171Scharnier * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3130171Scharnier * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3259656Siwasaki * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3359656Siwasaki * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3459656Siwasaki * SUCH DAMAGE.
3559656Siwasaki */
3659656Siwasaki
3730171Scharnier#ifndef lint
3810217Sphk#if 0
3910217Sphkstatic const char sccsid[] = "@(#)apply.c	8.4 (Berkeley) 4/4/94";
4010217Sphk#endif
4130171Scharnierstatic const char rcsid[] =
4210217Sphk  "$FreeBSD: head/usr.bin/apply/apply.c 82057 2001-08-21 12:54:15Z brian $";
4331290Snate#endif /* not lint */
4410217Sphk
4510217Sphk#include <sys/types.h>
4610217Sphk
4710217Sphk#include <sys/wait.h>
4816487Snate
4916487Snate#include <ctype.h>
5016487Snate#include <err.h>
5116487Snate#include <paths.h>
5216487Snate#include <signal.h>
5310217Sphk#include <stdio.h>
5416487Snate#include <stdlib.h>
55185124Simp#include <string.h>
56185124Simp#include <unistd.h>
57185124Simp
58185124Simp#define EXEC	"exec "
59185124Simp
60185124Simpstatic int	exec_shell(const char *, char *, char *);
61185124Simpstatic void	usage(void);
62185124Simp
63185124Simpint
64185124Simpmain(int argc, char *argv[]) {
65185124Simp	int ch, debug, i, magic, n, nargs, offset, rval;
66185124Simp	size_t clen, cmdsize, l;
67185124Simp	char *c, *cmd, *name, *p, *q, *shell, *slashp, *tmpshell;
68185124Simp
69185124Simp	debug = 0;
70185124Simp	magic = '%';		/* Default magic char is `%'. */
71185124Simp	nargs = -1;
72185124Simp	while ((ch = getopt(argc, argv, "a:d0123456789")) != -1)
73185124Simp		switch (ch) {
74185124Simp		case 'a':
75185124Simp			if (optarg[1] != '\0')
76185124Simp				errx(1,
77185124Simp				    "illegal magic character specification");
78185124Simp			magic = optarg[0];
79185124Simp			break;
80185124Simp		case 'd':
81185124Simp			debug = 1;
82185124Simp			break;
83185124Simp		case '0': case '1': case '2': case '3': case '4':
84185124Simp		case '5': case '6': case '7': case '8': case '9':
85185124Simp			if (nargs != -1)
86185124Simp				errx(1,
87185124Simp				    "only one -# argument may be specified");
88185124Simp			nargs = optopt - '0';
89185124Simp			break;
90185124Simp		default:
9115177Snate			usage();
9215177Snate		}
9310217Sphk	argc -= optind;
94185033Simp	argv += optind;
95185033Simp
96185033Simp	if (argc < 2)
97185033Simp		usage();
98185033Simp
99185033Simp	/*
100185033Simp	 * The command to run is argv[0], and the args are argv[1..].
101185033Simp	 * Look for %digit references in the command, remembering the
102185033Simp	 * largest one.
103185033Simp	 */
104185033Simp	for (n = 0, p = argv[0]; *p != '\0'; ++p)
105185033Simp		if (p[0] == magic && isdigit(p[1]) && p[1] != '0') {
106185033Simp			++p;
107185033Simp			if (p[0] - '0' > n)
10810217Sphk				n = p[0] - '0';
10910217Sphk		}
11010217Sphk
111185121Simp	/*
11210217Sphk	 * Figure out the shell and name arguments to pass to execl()
11310217Sphk	 * in exec_shell().  Always malloc() shell and just set name
11410217Sphk	 * to point at the last part of shell if there are any backslashes,
115185121Simp	 * otherwise just set it to point at the space malloc()'d.  If
11610217Sphk	 * SHELL environment variable exists, replace contents of
11715177Snate	 * shell with it.
11810217Sphk	 */
11910217Sphk	shell = name = NULL;
12010217Sphk	tmpshell = getenv("SHELL");
12110217Sphk	shell = (tmpshell != NULL) ? strdup(tmpshell) : strdup(_PATH_BSHELL);
122185121Simp	if (shell == NULL)
12310217Sphk		err(1, "strdup() failed");
124185121Simp	slashp = strrchr(shell, '/');
12515177Snate	name = (slashp != NULL) ? slashp + 1 : shell;
12610217Sphk
127185121Simp	/*
128185121Simp	 * If there were any %digit references, then use those, otherwise
12915177Snate	 * build a new command string with sufficient %digit references at
13010217Sphk	 * the end to consume (nargs) arguments each time round the loop.
131185121Simp	 * Allocate enough space to hold the maximum command.  Save the
132185121Simp	 * size to pass to snprintf().
13310217Sphk	 */
134185121Simp	cmdsize = sizeof(EXEC) - 1 + strlen(argv[0])
13515177Snate	    + 9 * (sizeof(" %1") - 1) + 1;
13610217Sphk	if ((cmd = malloc(cmdsize)) == NULL)
13715177Snate		err(1, NULL);
13810217Sphk
13959656Siwasaki	if (n == 0) {
14059656Siwasaki		/* If nargs not set, default to a single argument. */
14159656Siwasaki		if (nargs == -1)
14259656Siwasaki			nargs = 1;
14359656Siwasaki
14459656Siwasaki		p = cmd;
14559656Siwasaki		offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]);
14659656Siwasaki		if ((size_t)offset >= cmdsize)
14759656Siwasaki			err(1, "snprintf() failed");
14859656Siwasaki		p += offset;
14959656Siwasaki		cmdsize -= offset;
15059656Siwasaki		for (i = 1; i <= nargs; i++) {
15159656Siwasaki			offset = snprintf(p, cmdsize, " %c%d", magic, i);
15259656Siwasaki			if ((size_t)offset >= cmdsize)
15359656Siwasaki				err(1, "snprintf() failed");
15459656Siwasaki			p += offset;
15559656Siwasaki			cmdsize -= offset;
15659656Siwasaki		}
15759656Siwasaki
15859656Siwasaki		/*
15959656Siwasaki		 * If nargs set to the special value 0, eat a single
16059656Siwasaki		 * argument for each command execution.
16159656Siwasaki		 */
16259656Siwasaki		if (nargs == 0)
16359656Siwasaki			nargs = 1;
16459656Siwasaki	} else {
16559656Siwasaki		offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]);
16659656Siwasaki		if ((size_t)offset >= cmdsize)
16759656Siwasaki			err(1, "snprintf() failed");
16859656Siwasaki		nargs = n;
16959656Siwasaki	}
17059656Siwasaki
17159656Siwasaki	/*
17259656Siwasaki	 * Grab some space in which to build the command.  Allocate
17359656Siwasaki	 * as necessary later, but no reason to build it up slowly
17459656Siwasaki	 * for the normal case.
17510217Sphk	 */
17610217Sphk	if ((c = malloc(clen = 1024)) == NULL)
17710217Sphk		err(1, NULL);
17810217Sphk
17910217Sphk	/*
18010217Sphk	 * (argc) and (argv) are still offset by one to make it simpler to
18110217Sphk	 * expand %digit references.  At the end of the loop check for (argc)
18210217Sphk	 * equals 1 means that all the (argv) has been consumed.
18310217Sphk	 */
18410217Sphk	for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) {
18510217Sphk		/*
18610217Sphk		 * Find a max value for the command length, and ensure
18710217Sphk		 * there's enough space to build it.
18810217Sphk		 */
18910217Sphk		for (l = strlen(cmd), i = 0; i < nargs; i++)
19010217Sphk			l += strlen(argv[i+1]);
19110217Sphk		if (l > clen && (c = realloc(c, clen = l)) == NULL)
19216487Snate			err(1, NULL);
19310217Sphk
19410217Sphk		/* Expand command argv references. */
19515177Snate		for (p = cmd, q = c; *p != '\0'; ++p)
19615177Snate			if (p[0] == magic && isdigit(p[1]) && p[1] != '0') {
19715177Snate				offset = snprintf(q, l, "%s",
19815177Snate				    argv[(++p)[0] - '0']);
19910217Sphk				if ((size_t)offset >= l)
20010217Sphk					err(1, "snprintf() failed");
20115177Snate				q += offset;
20215177Snate				l -= offset;
20315177Snate			} else
20415177Snate				*q++ = *p;
20510217Sphk
20610217Sphk		/* Terminate the command string. */
20715177Snate		*q = '\0';
20810217Sphk
20910217Sphk		/* Run the command. */
21015177Snate		if (debug)
21115177Snate			(void)printf("%s\n", c);
21259656Siwasaki		else
21310217Sphk			if (exec_shell(c, shell, name))
214185121Simp				rval = 1;
21515177Snate	}
21610217Sphk
21715177Snate	if (argc != 1)
21815177Snate		errx(1, "expecting additional argument%s after \"%s\"",
21910217Sphk	    (nargs - argc) ? "s" : "", argv[argc - 1]);
22010217Sphk	free(cmd);
22110217Sphk	free(c);
22210217Sphk	free(shell);
22335310Snate	exit(rval);
22435310Snate}
22515177Snate
22615177Snate/*
22715177Snate * exec_shell --
22815177Snate * 	Execute a shell command using passed use_shell and use_name
22915177Snate * 	arguments.
23015177Snate */
231185124Simpstatic int
232185124Simpexec_shell(const char *command, char *use_shell, char *use_name)
23315177Snate{
234185121Simp	pid_t pid;
23510217Sphk	int omask, pstat;
236185121Simp	sig_t intsave, quitsave;
23715177Snate
23810217Sphk	if (!command)		/* just checking... */
239185121Simp		return(1);
24015177Snate
24115177Snate	omask = sigblock(sigmask(SIGCHLD));
24210217Sphk	switch(pid = vfork()) {
24315177Snate	case -1:			/* error */
24410217Sphk		err(1, "vfork");
24510217Sphk	case 0:				/* child */
24610217Sphk		(void)sigsetmask(omask);
24716487Snate		execl(use_shell, use_name, "-c", command, (char *)NULL);
24810217Sphk		warn("%s", use_shell);
24910217Sphk		_exit(1);
25015177Snate	}
25115177Snate	intsave = signal(SIGINT, SIG_IGN);
25215177Snate	quitsave = signal(SIGQUIT, SIG_IGN);
25315177Snate	pid = waitpid(pid, &pstat, 0);
25415177Snate	(void)sigsetmask(omask);
25510217Sphk	(void)signal(SIGINT, intsave);
25615177Snate	(void)signal(SIGQUIT, quitsave);
25710217Sphk	return(pid == -1 ? -1 : pstat);
25810217Sphk}
25915177Snate
26010217Sphkvoid
26110217Sphkusage()
26210217Sphk{
263185123Simp
26410217Sphk	(void)fprintf(stderr,
26515177Snate	"usage: apply [-a magic] [-d] [-0123456789] command arguments ...\n");
266185033Simp	exit(1);
26730171Scharnier}
26810217Sphk