1 /* $OpenBSD: apply.c,v 1.29 2018/04/01 17:45:05 bluhm 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 #include <sys/wait.h>
37
38 #include <ctype.h>
39 #include <err.h>
40 #include <paths.h>
41 #include <signal.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #define ISMAGICNO(p) \
48 (p)[0] == magic && isdigit((unsigned char)(p)[1]) && (p)[1] != '0'
49
50 __dead void usage(void);
51 static int mysystem(const char *);
52
53 char *str;
54 size_t sz;
55
56 void
stradd(char * p)57 stradd(char *p)
58 {
59 size_t n;
60
61 n = strlen(p);
62 if (str == NULL) {
63 sz = (n / 1024 + 1) * 1024;
64 if ((str = malloc(sz)) == NULL)
65 err(1, "malloc");
66 *str = '\0';
67 } else if (sz - strlen(str) <= n) {
68 sz += (n / 1024 + 1) * 1024;
69 if ((str = realloc(str, sz)) == NULL)
70 err(1, "realloc");
71 }
72 strlcat(str, p, sz);
73 }
74
75 void
strset(char * p)76 strset(char *p)
77 {
78 if (str != NULL)
79 str[0] = '\0';
80 stradd(p);
81 }
82
83 int
main(int argc,char * argv[])84 main(int argc, char *argv[])
85 {
86 int ch, debug, i, magic, n, nargs, rval;
87 char buf[4], *cmd, *p;
88
89 if (pledge("stdio proc exec", NULL) == -1)
90 err(1, "pledge");
91
92 debug = 0;
93 magic = '%'; /* Default magic char is `%'. */
94 nargs = -1;
95 while ((ch = getopt(argc, argv, "a:d0123456789")) != -1)
96 switch (ch) {
97 case 'a':
98 if (optarg[0] == '\0' || optarg[1] != '\0')
99 errx(1,
100 "illegal magic character specification.");
101 magic = optarg[0];
102 break;
103 case 'd':
104 debug = 1;
105 break;
106 case '0': case '1': case '2': case '3': case '4':
107 case '5': case '6': case '7': case '8': case '9':
108 if (nargs != -1)
109 errx(1,
110 "only one -# argument may be specified.");
111 nargs = ch - '0';
112 break;
113 default:
114 usage();
115 }
116 argc -= optind;
117 argv += optind;
118
119 if (argc < 2)
120 usage();
121
122 /*
123 * The command to run is argv[0], and the args are argv[1..].
124 * Look for %digit references in the command, remembering the
125 * largest one.
126 */
127 for (n = 0, p = argv[0]; *p != '\0'; ++p)
128 if (ISMAGICNO(p)) {
129 ++p;
130 if (p[0] - '0' > n)
131 n = p[0] - '0';
132 }
133
134 /*
135 * If there were any %digit references, then use those, otherwise
136 * build a new command string with sufficient %digit references at
137 * the end to consume (nargs) arguments each time round the loop.
138 * Allocate enough space to hold the maximum command.
139 */
140 strset(argv[0]);
141 if (n == 0) {
142 /* If nargs not set, default to a single argument. */
143 if (nargs == -1)
144 nargs = 1;
145
146 for (i = 1; i <= nargs; i++) {
147 snprintf(buf, sizeof(buf), " %c%d", magic, i);
148 stradd(buf);
149 }
150
151 /*
152 * If nargs set to the special value 0, eat a single
153 * argument for each command execution.
154 */
155 if (nargs == 0)
156 nargs = 1;
157 } else
158 nargs = n;
159 if ((cmd = strdup(str)) == NULL)
160 err(1, "strdup");
161
162 /*
163 * (argc) and (argv) are still offset by one to make it simpler to
164 * expand %digit references. At the end of the loop check for (argc)
165 * equals 1 means that all the (argv) has been consumed.
166 */
167 for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) {
168 strset("exec ");
169
170 /* Expand command argv references. */
171 for (p = cmd; *p != '\0'; ++p)
172 if (ISMAGICNO(p))
173 stradd(argv[*(++p) - '0']);
174 else {
175 strlcpy(buf, p, 2);
176 stradd(buf);
177 }
178
179 /* Run the command. */
180 if (debug)
181 (void)printf("%s\n", str);
182 else if (mysystem(str))
183 rval = 1;
184 }
185
186 if (argc != 1)
187 errx(1, "expecting additional argument%s after \"%s\"",
188 (nargs - argc) ? "s" : "", argv[argc - 1]);
189 exit(rval);
190 }
191
192 /*
193 * mysystem --
194 * Private version of system(3). Use the user's SHELL environment
195 * variable as the shell to execute.
196 */
197 static int
mysystem(const char * command)198 mysystem(const char *command)
199 {
200 static const char *name, *shell;
201 pid_t pid;
202 int pstat;
203 sigset_t mask, omask;
204 sig_t intsave, quitsave;
205
206 if (shell == NULL) {
207 if ((shell = getenv("SHELL")) == NULL)
208 shell = _PATH_BSHELL;
209 if ((name = strrchr(shell, '/')) == NULL)
210 name = shell;
211 else
212 ++name;
213 }
214 if (!command) /* just checking... */
215 return(1);
216
217 sigemptyset(&mask);
218 sigaddset(&mask, SIGCHLD);
219 sigprocmask(SIG_BLOCK, &mask, &omask);
220 switch(pid = fork()) {
221 case -1: /* error */
222 err(1, "fork");
223 case 0: /* child */
224 sigprocmask(SIG_SETMASK, &omask, NULL);
225 execl(shell, name, "-c", command, (char *)NULL);
226 err(1, "%s", shell);
227 }
228 intsave = signal(SIGINT, SIG_IGN);
229 quitsave = signal(SIGQUIT, SIG_IGN);
230 pid = waitpid(pid, &pstat, 0);
231 sigprocmask(SIG_SETMASK, &omask, NULL);
232 (void)signal(SIGINT, intsave);
233 (void)signal(SIGQUIT, quitsave);
234 return(pid == -1 ? -1 : pstat);
235 }
236
237 __dead void
usage(void)238 usage(void)
239 {
240 (void)fprintf(stderr,
241 "usage: apply [-#] [-d] [-a magic] command argument ...\n");
242 exit(1);
243 }
244