]>
Commit | Line | Data |
---|---|---|
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 | #if 0 | |
38 | #ifndef lint | |
39 | static char sccsid[] = "@(#)apply.c 8.4 (Berkeley) 4/4/94"; | |
40 | #endif | |
41 | #endif | |
42 | ||
43 | #include <sys/cdefs.h> | |
44 | __RCSID("$FreeBSD: src/usr.bin/apply/apply.c,v 1.22 2002/07/14 18:22:12 alfred Exp $"); | |
45 | ||
46 | #include <sys/types.h> | |
47 | #include <sys/wait.h> | |
48 | ||
49 | #include <ctype.h> | |
50 | #include <err.h> | |
51 | #include <paths.h> | |
52 | #include <signal.h> | |
53 | #include <stdio.h> | |
54 | #include <stdlib.h> | |
55 | #include <string.h> | |
56 | #include <unistd.h> | |
57 | ||
58 | #define EXEC "exec " | |
59 | ||
60 | static int exec_shell(const char *, char *, char *); | |
61 | static void usage(void); | |
62 | ||
63 | int | |
64 | main(int argc, char *argv[]) { | |
65 | int ch, debug, i, magic, n, nargs, offset, rval; | |
66 | size_t clen, cmdsize, l; | |
67 | char *c, *cmd, *name, *p, *q, *shell, *slashp, *tmpshell; | |
68 | ||
69 | debug = 0; | |
70 | magic = '%'; /* Default magic char is `%'. */ | |
71 | nargs = -1; | |
72 | while ((ch = getopt(argc, argv, "a:d0123456789")) != -1) | |
73 | switch (ch) { | |
74 | case 'a': | |
75 | if (optarg[1] != '\0') | |
76 | errx(1, | |
77 | "illegal magic character specification"); | |
78 | magic = optarg[0]; | |
79 | break; | |
80 | case 'd': | |
81 | debug = 1; | |
82 | break; | |
83 | case '0': case '1': case '2': case '3': case '4': | |
84 | case '5': case '6': case '7': case '8': case '9': | |
85 | if (nargs != -1) | |
86 | errx(1, | |
87 | "only one -# argument may be specified"); | |
88 | nargs = optopt - '0'; | |
89 | break; | |
90 | default: | |
91 | usage(); | |
92 | } | |
93 | argc -= optind; | |
94 | argv += optind; | |
95 | ||
96 | if (argc < 2) | |
97 | usage(); | |
98 | ||
99 | /* | |
100 | * The command to run is argv[0], and the args are argv[1..]. | |
101 | * Look for %digit references in the command, remembering the | |
102 | * largest one. | |
103 | */ | |
104 | for (n = 0, p = argv[0]; *p != '\0'; ++p) | |
105 | if (p[0] == magic && isdigit(p[1]) && p[1] != '0') { | |
106 | ++p; | |
107 | if (p[0] - '0' > n) | |
108 | n = p[0] - '0'; | |
109 | } | |
110 | ||
111 | /* | |
112 | * Figure out the shell and name arguments to pass to execl() | |
113 | * in exec_shell(). Always malloc() shell and just set name | |
114 | * to point at the last part of shell if there are any backslashes, | |
115 | * otherwise just set it to point at the space malloc()'d. If | |
116 | * SHELL environment variable exists, replace contents of | |
117 | * shell with it. | |
118 | */ | |
119 | shell = name = NULL; | |
120 | tmpshell = getenv("SHELL"); | |
121 | shell = (tmpshell != NULL) ? strdup(tmpshell) : strdup(_PATH_BSHELL); | |
122 | if (shell == NULL) | |
123 | err(1, "strdup() failed"); | |
124 | slashp = strrchr(shell, '/'); | |
125 | name = (slashp != NULL) ? slashp + 1 : shell; | |
126 | ||
127 | /* | |
128 | * If there were any %digit references, then use those, otherwise | |
129 | * build a new command string with sufficient %digit references at | |
130 | * the end to consume (nargs) arguments each time round the loop. | |
131 | * Allocate enough space to hold the maximum command. Save the | |
132 | * size to pass to snprintf(). | |
133 | */ | |
134 | cmdsize = sizeof(EXEC) - 1 + strlen(argv[0]) | |
135 | + 9 * (sizeof(" %1") - 1) + 1; | |
136 | if ((cmd = malloc(cmdsize)) == NULL) | |
137 | err(1, NULL); | |
138 | ||
139 | if (n == 0) { | |
140 | /* If nargs not set, default to a single argument. */ | |
141 | if (nargs == -1) | |
142 | nargs = 1; | |
143 | ||
144 | p = cmd; | |
145 | offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]); | |
146 | if ((size_t)offset >= cmdsize) | |
147 | err(1, "snprintf() failed"); | |
148 | p += offset; | |
149 | cmdsize -= offset; | |
150 | for (i = 1; i <= nargs; i++) { | |
151 | offset = snprintf(p, cmdsize, " %c%d", magic, i); | |
152 | if ((size_t)offset >= cmdsize) | |
153 | err(1, "snprintf() failed"); | |
154 | p += offset; | |
155 | cmdsize -= offset; | |
156 | } | |
157 | ||
158 | /* | |
159 | * If nargs set to the special value 0, eat a single | |
160 | * argument for each command execution. | |
161 | */ | |
162 | if (nargs == 0) | |
163 | nargs = 1; | |
164 | } else { | |
165 | offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]); | |
166 | if ((size_t)offset >= cmdsize) | |
167 | err(1, "snprintf() failed"); | |
168 | nargs = n; | |
169 | } | |
170 | ||
171 | /* | |
172 | * Grab some space in which to build the command. Allocate | |
173 | * as necessary later, but no reason to build it up slowly | |
174 | * for the normal case. | |
175 | */ | |
176 | if ((c = malloc(clen = 1024)) == NULL) | |
177 | err(1, NULL); | |
178 | ||
179 | /* | |
180 | * (argc) and (argv) are still offset by one to make it simpler to | |
181 | * expand %digit references. At the end of the loop check for (argc) | |
182 | * equals 1 means that all the (argv) has been consumed. | |
183 | */ | |
184 | for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) { | |
185 | /* | |
186 | * Find a max value for the command length, and ensure | |
187 | * there's enough space to build it. | |
188 | */ | |
189 | for (l = strlen(cmd), i = 0; i < nargs; i++) | |
190 | l += strlen(argv[i+1]); | |
191 | if (l > clen && (c = realloc(c, clen = l)) == NULL) | |
192 | err(1, NULL); | |
193 | ||
194 | /* Expand command argv references. */ | |
195 | for (p = cmd, q = c; *p != '\0'; ++p) | |
196 | if (p[0] == magic && isdigit(p[1]) && p[1] != '0') { | |
197 | offset = snprintf(q, l, "%s", | |
198 | argv[(++p)[0] - '0']); | |
199 | if ((size_t)offset >= l) | |
200 | err(1, "snprintf() failed"); | |
201 | q += offset; | |
202 | l -= offset; | |
203 | } else | |
204 | *q++ = *p; | |
205 | ||
206 | /* Terminate the command string. */ | |
207 | *q = '\0'; | |
208 | ||
209 | /* Run the command. */ | |
210 | if (debug) | |
211 | (void)printf("%s\n", c); | |
212 | else | |
213 | if (exec_shell(c, shell, name)) | |
214 | rval = 1; | |
215 | } | |
216 | ||
217 | if (argc != 1) | |
218 | errx(1, "expecting additional argument%s after \"%s\"", | |
219 | (nargs - argc) ? "s" : "", argv[argc - 1]); | |
220 | free(cmd); | |
221 | free(c); | |
222 | free(shell); | |
223 | exit(rval); | |
224 | } | |
225 | ||
226 | /* | |
227 | * exec_shell -- | |
228 | * Execute a shell command using passed use_shell and use_name | |
229 | * arguments. | |
230 | */ | |
231 | static int | |
232 | exec_shell(const char *command, char *use_shell, char *use_name) | |
233 | { | |
234 | pid_t pid; | |
235 | int omask, pstat; | |
236 | sig_t intsave, quitsave; | |
237 | ||
238 | if (!command) /* just checking... */ | |
239 | return(1); | |
240 | ||
241 | omask = sigblock(sigmask(SIGCHLD)); | |
242 | switch(pid = vfork()) { | |
243 | case -1: /* error */ | |
244 | err(1, "vfork"); | |
245 | case 0: /* child */ | |
246 | (void)sigsetmask(omask); | |
247 | execl(use_shell, use_name, "-c", command, (char *)NULL); | |
248 | warn("%s", use_shell); | |
249 | _exit(1); | |
250 | } | |
251 | intsave = signal(SIGINT, SIG_IGN); | |
252 | quitsave = signal(SIGQUIT, SIG_IGN); | |
253 | pid = waitpid(pid, &pstat, 0); | |
254 | (void)sigsetmask(omask); | |
255 | (void)signal(SIGINT, intsave); | |
256 | (void)signal(SIGQUIT, quitsave); | |
257 | return(pid == -1 ? -1 : pstat); | |
258 | } | |
259 | ||
260 | void | |
261 | usage(void) | |
262 | { | |
263 | ||
264 | (void)fprintf(stderr, | |
265 | "usage: apply [-a magic] [-d] [-0123456789] command arguments ...\n"); | |
266 | exit(1); | |
267 | } |