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