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