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