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