1 /* $OpenBSD: apply.c,v 1.25 2011/04/29 05:45:11 lum 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 && isdigit(p[1]) && p[1] != '0') { 94 ++p; 95 if (p[0] - '0' > n) 96 n = p[0] - '0'; 97 } 98 99 /* 100 * If there were any %digit references, then use those, otherwise 101 * build a new command string with sufficient %digit references at 102 * the end to consume (nargs) arguments each time round the loop. 103 * Allocate enough space to hold the maximum command. 104 */ 105 if (n == 0) { 106 len = sizeof("exec ") - 1 + 107 strlen(argv[0]) + 9 * (sizeof(" %1") - 1) + 1; 108 if ((cmd = malloc(len)) == NULL) 109 err(1, NULL); 110 111 /* If nargs not set, default to a single argument. */ 112 if (nargs == -1) 113 nargs = 1; 114 115 l = snprintf(cmd, len, "exec %s", argv[0]); 116 if (l >= len || l == -1) 117 errx(1, "error building exec string"); 118 len -= l; 119 p = cmd + l; 120 121 for (i = 1; i <= nargs; i++) { 122 l = snprintf(p, len, " %c%d", magic, i); 123 if (l >= len || l == -1) 124 errx(1, "error numbering arguments"); 125 len -= l; 126 p += l; 127 } 128 129 /* 130 * If nargs set to the special value 0, eat a single 131 * argument for each command execution. 132 */ 133 if (nargs == 0) 134 nargs = 1; 135 } else { 136 if (asprintf(&cmd, "exec %s", argv[0]) == -1) 137 err(1, NULL); 138 nargs = n; 139 } 140 141 /* 142 * Grab some space in which to build the command. Allocate 143 * as necessary later, but no reason to build it up slowly 144 * for the normal case. 145 */ 146 if ((c = malloc(clen = 1024)) == NULL) 147 err(1, NULL); 148 149 /* 150 * (argc) and (argv) are still offset by one to make it simpler to 151 * expand %digit references. At the end of the loop check for (argc) 152 * equals 1 means that all the (argv) has been consumed. 153 */ 154 for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) { 155 /* 156 * Find a max value for the command length, and ensure 157 * there's enough space to build it. 158 */ 159 for (l = strlen(cmd), i = 0; i < nargs; i++) 160 l += strlen(argv[i+1]); 161 if (l > clen) { 162 if ((c2 = realloc(c, l)) == NULL) 163 err(1, NULL); 164 c = c2; 165 clen = l; 166 } 167 168 /* Expand command argv references. */ 169 for (p = cmd, q = c; *p != '\0'; ++p) 170 if (p[0] == magic && isdigit(p[1]) && p[1] != '0') { 171 strlcpy(q, argv[(++p)[0] - '0'], c + clen - q); 172 q += strlen(q); 173 } else 174 *q++ = *p; 175 176 /* Terminate the command string. */ 177 *q = '\0'; 178 179 /* Run the command. */ 180 if (debug) 181 (void)printf("%s\n", c); 182 else if (mysystem(c)) 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