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