apply.c revision 204761
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 * 3. All advertising materials mentioning features or use of this software
171590Srgrimes *    must display the following acknowledgement:
181590Srgrimes *	This product includes software developed by the University of
191590Srgrimes *	California, Berkeley and its contributors.
201590Srgrimes * 4. Neither the name of the University nor the names of its contributors
211590Srgrimes *    may be used to endorse or promote products derived from this software
221590Srgrimes *    without specific prior written permission.
231590Srgrimes *
241590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341590Srgrimes * SUCH DAMAGE.
351590Srgrimes */
361590Srgrimes
3787628Sdwmalone#if 0
3887628Sdwmalone#ifndef lint
3987628Sdwmalonestatic char sccsid[] = "@(#)apply.c	8.4 (Berkeley) 4/4/94";
4087628Sdwmalone#endif
4187628Sdwmalone#endif
4287628Sdwmalone
4387231Smarkm#include <sys/cdefs.h>
4487231Smarkm__FBSDID("$FreeBSD: head/usr.bin/apply/apply.c 204761 2010-03-05 15:23:01Z jh $");
4587231Smarkm
4667189Sbrian#include <sys/types.h>
47204761Sjh#include <sys/sbuf.h>
481590Srgrimes#include <sys/wait.h>
491590Srgrimes
501590Srgrimes#include <ctype.h>
511590Srgrimes#include <err.h>
52204761Sjh#include <errno.h>
531590Srgrimes#include <paths.h>
541590Srgrimes#include <signal.h>
551590Srgrimes#include <stdio.h>
561590Srgrimes#include <stdlib.h>
571590Srgrimes#include <string.h>
581590Srgrimes#include <unistd.h>
591590Srgrimes
6071326Swill#define EXEC	"exec "
611590Srgrimes
6271326Swillstatic int	exec_shell(const char *, char *, char *);
6371326Swillstatic void	usage(void);
6470692Swill
651590Srgrimesint
66204761Sjhmain(int argc, char *argv[])
67204761Sjh{
68204761Sjh	struct sbuf *cmdbuf;
69204761Sjh	long arg_max;
7071326Swill	int ch, debug, i, magic, n, nargs, offset, rval;
71204761Sjh	size_t cmdsize;
72204761Sjh	char *cmd, *name, *p, *shell, *slashp, *tmpshell;
731590Srgrimes
741590Srgrimes	debug = 0;
751590Srgrimes	magic = '%';		/* Default magic char is `%'. */
761590Srgrimes	nargs = -1;
7770672Swill	while ((ch = getopt(argc, argv, "a:d0123456789")) != -1)
781590Srgrimes		switch (ch) {
791590Srgrimes		case 'a':
801590Srgrimes			if (optarg[1] != '\0')
811590Srgrimes				errx(1,
8254157Scharnier				    "illegal magic character specification");
831590Srgrimes			magic = optarg[0];
841590Srgrimes			break;
851590Srgrimes		case 'd':
861590Srgrimes			debug = 1;
871590Srgrimes			break;
881590Srgrimes		case '0': case '1': case '2': case '3': case '4':
891590Srgrimes		case '5': case '6': case '7': case '8': case '9':
901590Srgrimes			if (nargs != -1)
911590Srgrimes				errx(1,
9254157Scharnier				    "only one -# argument may be specified");
931590Srgrimes			nargs = optopt - '0';
941590Srgrimes			break;
951590Srgrimes		default:
961590Srgrimes			usage();
971590Srgrimes		}
981590Srgrimes	argc -= optind;
991590Srgrimes	argv += optind;
1001590Srgrimes
1011590Srgrimes	if (argc < 2)
1021590Srgrimes		usage();
1031590Srgrimes
1041590Srgrimes	/*
1051590Srgrimes	 * The command to run is argv[0], and the args are argv[1..].
1061590Srgrimes	 * Look for %digit references in the command, remembering the
1071590Srgrimes	 * largest one.
1081590Srgrimes	 */
1091590Srgrimes	for (n = 0, p = argv[0]; *p != '\0'; ++p)
1101590Srgrimes		if (p[0] == magic && isdigit(p[1]) && p[1] != '0') {
1111590Srgrimes			++p;
1121590Srgrimes			if (p[0] - '0' > n)
1131590Srgrimes				n = p[0] - '0';
1141590Srgrimes		}
1151590Srgrimes
1161590Srgrimes	/*
11770692Swill	 * Figure out the shell and name arguments to pass to execl()
11870692Swill	 * in exec_shell().  Always malloc() shell and just set name
11970692Swill	 * to point at the last part of shell if there are any backslashes,
12070692Swill	 * otherwise just set it to point at the space malloc()'d.  If
12170692Swill	 * SHELL environment variable exists, replace contents of
12270692Swill	 * shell with it.
12370692Swill	 */
12470692Swill	shell = name = NULL;
12570692Swill	tmpshell = getenv("SHELL");
12670692Swill	shell = (tmpshell != NULL) ? strdup(tmpshell) : strdup(_PATH_BSHELL);
12770692Swill	if (shell == NULL)
12870692Swill		err(1, "strdup() failed");
12970692Swill	slashp = strrchr(shell, '/');
13070692Swill	name = (slashp != NULL) ? slashp + 1 : shell;
13170692Swill
13270692Swill	/*
1331590Srgrimes	 * If there were any %digit references, then use those, otherwise
1341590Srgrimes	 * build a new command string with sufficient %digit references at
1351590Srgrimes	 * the end to consume (nargs) arguments each time round the loop.
13670692Swill	 * Allocate enough space to hold the maximum command.  Save the
13770692Swill	 * size to pass to snprintf().
1381590Srgrimes	 */
13971326Swill	cmdsize = sizeof(EXEC) - 1 + strlen(argv[0])
14071326Swill	    + 9 * (sizeof(" %1") - 1) + 1;
14170692Swill	if ((cmd = malloc(cmdsize)) == NULL)
1421590Srgrimes		err(1, NULL);
1438874Srgrimes
1441590Srgrimes	if (n == 0) {
1451590Srgrimes		/* If nargs not set, default to a single argument. */
1461590Srgrimes		if (nargs == -1)
1471590Srgrimes			nargs = 1;
1481590Srgrimes
1491590Srgrimes		p = cmd;
15070692Swill		offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]);
15182057Sbrian		if ((size_t)offset >= cmdsize)
152204761Sjh			errx(1, "snprintf() failed");
15370692Swill		p += offset;
15471615Swill		cmdsize -= offset;
15570692Swill		for (i = 1; i <= nargs; i++) {
15670692Swill			offset = snprintf(p, cmdsize, " %c%d", magic, i);
15782057Sbrian			if ((size_t)offset >= cmdsize)
158204761Sjh				errx(1, "snprintf() failed");
15970692Swill			p += offset;
16071615Swill			cmdsize -= offset;
16170692Swill		}
1621590Srgrimes
1631590Srgrimes		/*
1641590Srgrimes		 * If nargs set to the special value 0, eat a single
1651590Srgrimes		 * argument for each command execution.
1661590Srgrimes		 */
1671590Srgrimes		if (nargs == 0)
1681590Srgrimes			nargs = 1;
1691590Srgrimes	} else {
17070692Swill		offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]);
17182057Sbrian		if ((size_t)offset >= cmdsize)
172204761Sjh			errx(1, "snprintf() failed");
1731590Srgrimes		nargs = n;
1741590Srgrimes	}
1751590Srgrimes
176204761Sjh	cmdbuf = sbuf_new(NULL, NULL, 1024, SBUF_AUTOEXTEND);
177204761Sjh	if (cmdbuf == NULL)
1781590Srgrimes		err(1, NULL);
1791590Srgrimes
180204761Sjh	arg_max = sysconf(_SC_ARG_MAX);
181204761Sjh
1821590Srgrimes	/*
1831590Srgrimes	 * (argc) and (argv) are still offset by one to make it simpler to
1841590Srgrimes	 * expand %digit references.  At the end of the loop check for (argc)
1851590Srgrimes	 * equals 1 means that all the (argv) has been consumed.
1861590Srgrimes	 */
1871590Srgrimes	for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) {
188204761Sjh		sbuf_clear(cmdbuf);
1891590Srgrimes		/* Expand command argv references. */
190204761Sjh		for (p = cmd; *p != '\0'; ++p) {
19170692Swill			if (p[0] == magic && isdigit(p[1]) && p[1] != '0') {
192204761Sjh				if (sbuf_cat(cmdbuf, argv[(++p)[0] - '0'])
193204761Sjh				    == -1)
194204761Sjh					errc(1, ENOMEM, "sbuf");
195204761Sjh			} else {
196204761Sjh				if (sbuf_putc(cmdbuf, *p) == -1)
197204761Sjh					errc(1, ENOMEM, "sbuf");
198204761Sjh			}
199204761Sjh			if (sbuf_len(cmdbuf) > arg_max)
200204761Sjh				errc(1, E2BIG, NULL);
201204761Sjh		}
2021590Srgrimes
2031590Srgrimes		/* Terminate the command string. */
204204761Sjh		sbuf_finish(cmdbuf);
2051590Srgrimes
2061590Srgrimes		/* Run the command. */
2071590Srgrimes		if (debug)
208204761Sjh			(void)printf("%s\n", sbuf_data(cmdbuf));
2091590Srgrimes		else
210204761Sjh			if (exec_shell(sbuf_data(cmdbuf), shell, name))
2111590Srgrimes				rval = 1;
2121590Srgrimes	}
2131590Srgrimes
2141590Srgrimes	if (argc != 1)
2151590Srgrimes		errx(1, "expecting additional argument%s after \"%s\"",
216204761Sjh		    (nargs - argc) ? "s" : "", argv[argc - 1]);
21770692Swill	free(cmd);
218204761Sjh	sbuf_delete(cmdbuf);
21970692Swill	free(shell);
2201590Srgrimes	exit(rval);
2211590Srgrimes}
2221590Srgrimes
2231590Srgrimes/*
22470692Swill * exec_shell --
22570692Swill * 	Execute a shell command using passed use_shell and use_name
22670692Swill * 	arguments.
2271590Srgrimes */
22870692Swillstatic int
22971326Swillexec_shell(const char *command, char *use_shell, char *use_name)
2301590Srgrimes{
2311590Srgrimes	pid_t pid;
23243928Seivind	int omask, pstat;
2331590Srgrimes	sig_t intsave, quitsave;
2341590Srgrimes
23570672Swill	if (!command)		/* just checking... */
2361590Srgrimes		return(1);
2371590Srgrimes
2381590Srgrimes	omask = sigblock(sigmask(SIGCHLD));
23960706Skris	switch(pid = vfork()) {
2401590Srgrimes	case -1:			/* error */
24160706Skris		err(1, "vfork");
2421590Srgrimes	case 0:				/* child */
2431590Srgrimes		(void)sigsetmask(omask);
24479452Sbrian		execl(use_shell, use_name, "-c", command, (char *)NULL);
24570692Swill		warn("%s", use_shell);
24660706Skris		_exit(1);
2471590Srgrimes	}
2481590Srgrimes	intsave = signal(SIGINT, SIG_IGN);
2491590Srgrimes	quitsave = signal(SIGQUIT, SIG_IGN);
25043928Seivind	pid = waitpid(pid, &pstat, 0);
2511590Srgrimes	(void)sigsetmask(omask);
2521590Srgrimes	(void)signal(SIGINT, intsave);
2531590Srgrimes	(void)signal(SIGQUIT, quitsave);
25443928Seivind	return(pid == -1 ? -1 : pstat);
2551590Srgrimes}
2561590Srgrimes
2571590Srgrimesvoid
25899984Salfredusage(void)
2591590Srgrimes{
2601590Srgrimes
2611590Srgrimes	(void)fprintf(stderr,
26270672Swill	"usage: apply [-a magic] [-d] [-0123456789] command arguments ...\n");
2631590Srgrimes	exit(1);
2641590Srgrimes}
265