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 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 76 strset(char *p) 77 { 78 if (str != NULL) 79 str[0] = '\0'; 80 stradd(p); 81 } 82 83 int 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 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 238 usage(void) 239 { 240 (void)fprintf(stderr, 241 "usage: apply [-#] [-d] [-a magic] command argument ...\n"); 242 exit(1); 243 } 244