apply.c revision 71615
1106163Sroberto/*- 2106163Sroberto * Copyright (c) 1994 3106163Sroberto * The Regents of the University of California. All rights reserved. 4106163Sroberto * 5106163Sroberto * This code is derived from software contributed to Berkeley by 6106163Sroberto * Jan-Simon Pendry. 7106163Sroberto * 8106163Sroberto * Redistribution and use in source and binary forms, with or without 9106163Sroberto * modification, are permitted provided that the following conditions 10106163Sroberto * are met: 11106163Sroberto * 1. Redistributions of source code must retain the above copyright 12106163Sroberto * notice, this list of conditions and the following disclaimer. 13106163Sroberto * 2. Redistributions in binary form must reproduce the above copyright 14106163Sroberto * notice, this list of conditions and the following disclaimer in the 15106163Sroberto * documentation and/or other materials provided with the distribution. 16106163Sroberto * 3. All advertising materials mentioning features or use of this software 17106163Sroberto * must display the following acknowledgement: 18106163Sroberto * This product includes software developed by the University of 19106163Sroberto * California, Berkeley and its contributors. 20106163Sroberto * 4. Neither the name of the University nor the names of its contributors 21106163Sroberto * may be used to endorse or promote products derived from this software 22290000Sglebius * without specific prior written permission. 23290000Sglebius * 24290000Sglebius * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25106163Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26106163Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27106163Sroberto * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28106163Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29106163Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30106163Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31106163Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32106163Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33106163Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34106163Sroberto * SUCH DAMAGE. 35106163Sroberto */ 36106163Sroberto 37106163Sroberto#ifndef lint 38106163Sroberto#if 0 39106163Srobertostatic const char sccsid[] = "@(#)apply.c 8.4 (Berkeley) 4/4/94"; 40106163Sroberto#endif 41106163Srobertostatic const char rcsid[] = 42106163Sroberto "$FreeBSD: head/usr.bin/apply/apply.c 71615 2001-01-25 03:40:17Z will $"; 43106163Sroberto#endif /* not lint */ 44106163Sroberto 45106163Sroberto#include <sys/types.h> 46106163Sroberto 47106163Sroberto#include <sys/wait.h> 48106163Sroberto 49106163Sroberto#include <ctype.h> 50106163Sroberto#include <err.h> 51106163Sroberto#include <paths.h> 52106163Sroberto#include <signal.h> 53106163Sroberto#include <stdio.h> 54106163Sroberto#include <stdlib.h> 55106163Sroberto#include <string.h> 56106163Sroberto#include <unistd.h> 57106163Sroberto 58106163Sroberto#define EXEC "exec " 59106163Sroberto 60106163Srobertostatic int exec_shell(const char *, char *, char *); 61106163Srobertostatic void usage(void); 62106163Sroberto 63106163Srobertoint 64106163Srobertomain(int argc, char *argv[]) { 65106163Sroberto int ch, debug, i, magic, n, nargs, offset, rval; 66106163Sroberto size_t clen, cmdsize, l; 67106163Sroberto char *c, *cmd, *name, *p, *q, *shell, *slashp, *tmpshell; 68106163Sroberto 69106163Sroberto debug = 0; 70106163Sroberto magic = '%'; /* Default magic char is `%'. */ 71106163Sroberto nargs = -1; 72106163Sroberto while ((ch = getopt(argc, argv, "a:d0123456789")) != -1) 73106163Sroberto switch (ch) { 74106163Sroberto case 'a': 75106163Sroberto if (optarg[1] != '\0') 76106163Sroberto errx(1, 77106163Sroberto "illegal magic character specification"); 78106163Sroberto magic = optarg[0]; 79106163Sroberto break; 80106163Sroberto case 'd': 81106163Sroberto debug = 1; 82106163Sroberto break; 83106163Sroberto case '0': case '1': case '2': case '3': case '4': 84106163Sroberto case '5': case '6': case '7': case '8': case '9': 85106163Sroberto if (nargs != -1) 86106163Sroberto errx(1, 87106163Sroberto "only one -# argument may be specified"); 88106163Sroberto nargs = optopt - '0'; 89106163Sroberto break; 90106163Sroberto default: 91106163Sroberto usage(); 92106163Sroberto } 93106163Sroberto argc -= optind; 94106163Sroberto argv += optind; 95106163Sroberto 96106163Sroberto if (argc < 2) 97106163Sroberto usage(); 98106163Sroberto 99106163Sroberto /* 100106163Sroberto * The command to run is argv[0], and the args are argv[1..]. 101106163Sroberto * Look for %digit references in the command, remembering the 102290000Sglebius * largest one. 103290000Sglebius */ 104290000Sglebius for (n = 0, p = argv[0]; *p != '\0'; ++p) 105290000Sglebius if (p[0] == magic && isdigit(p[1]) && p[1] != '0') { 106106163Sroberto ++p; 107106163Sroberto if (p[0] - '0' > n) 108106163Sroberto n = p[0] - '0'; 109106163Sroberto } 110106163Sroberto 111106163Sroberto /* 112106163Sroberto * Figure out the shell and name arguments to pass to execl() 113106163Sroberto * in exec_shell(). Always malloc() shell and just set name 114106163Sroberto * to point at the last part of shell if there are any backslashes, 115106163Sroberto * otherwise just set it to point at the space malloc()'d. If 116106163Sroberto * SHELL environment variable exists, replace contents of 117106163Sroberto * shell with it. 118106163Sroberto */ 119106163Sroberto shell = name = NULL; 120106163Sroberto tmpshell = getenv("SHELL"); 121106163Sroberto shell = (tmpshell != NULL) ? strdup(tmpshell) : strdup(_PATH_BSHELL); 122106163Sroberto if (shell == NULL) 123106163Sroberto err(1, "strdup() failed"); 124106163Sroberto slashp = strrchr(shell, '/'); 125106163Sroberto name = (slashp != NULL) ? slashp + 1 : shell; 126106163Sroberto 127106163Sroberto /* 128106163Sroberto * If there were any %digit references, then use those, otherwise 129106163Sroberto * build a new command string with sufficient %digit references at 130106163Sroberto * the end to consume (nargs) arguments each time round the loop. 131106163Sroberto * Allocate enough space to hold the maximum command. Save the 132106163Sroberto * size to pass to snprintf(). 133106163Sroberto */ 134106163Sroberto cmdsize = sizeof(EXEC) - 1 + strlen(argv[0]) 135106163Sroberto + 9 * (sizeof(" %1") - 1) + 1; 136106163Sroberto if ((cmd = malloc(cmdsize)) == NULL) 137106163Sroberto err(1, NULL); 138106163Sroberto 139290000Sglebius if (n == 0) { 140290000Sglebius /* If nargs not set, default to a single argument. */ 141290000Sglebius if (nargs == -1) 142290000Sglebius nargs = 1; 143106163Sroberto 144106163Sroberto p = cmd; 145106163Sroberto offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]); 146106163Sroberto if ((size_t)offset >= cmdsize) 147106163Sroberto err(1, "snprintf() failed"); 148106163Sroberto p += offset; 149290000Sglebius cmdsize -= offset; 150290000Sglebius for (i = 1; i <= nargs; i++) { 151106163Sroberto offset = snprintf(p, cmdsize, " %c%d", magic, i); 152106163Sroberto if ((size_t)offset >= cmdsize) 153290000Sglebius err(1, "snprintf() failed"); 154106163Sroberto p += offset; 155106163Sroberto cmdsize -= offset; 156106163Sroberto } 157290000Sglebius 158290000Sglebius /* 159106163Sroberto * If nargs set to the special value 0, eat a single 160106163Sroberto * argument for each command execution. 161106163Sroberto */ 162290000Sglebius if (nargs == 0) 163106163Sroberto nargs = 1; 164106163Sroberto } else { 165106163Sroberto offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]); 166106163Sroberto if ((size_t)offset >= cmdsize) 167106163Sroberto err(1, "snprintf() failed"); 168106163Sroberto nargs = n; 169106163Sroberto } 170106163Sroberto 171106163Sroberto /* 172106163Sroberto * Grab some space in which to build the command. Allocate 173106163Sroberto * as necessary later, but no reason to build it up slowly 174106163Sroberto * for the normal case. 175106163Sroberto */ 176106163Sroberto if ((c = malloc(clen = 1024)) == NULL) 177106163Sroberto err(1, NULL); 178106163Sroberto 179106163Sroberto /* 180106163Sroberto * (argc) and (argv) are still offset by one to make it simpler to 181106163Sroberto * expand %digit references. At the end of the loop check for (argc) 182106163Sroberto * equals 1 means that all the (argv) has been consumed. 183106163Sroberto */ 184106163Sroberto for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) { 185106163Sroberto /* 186106163Sroberto * Find a max value for the command length, and ensure 187106163Sroberto * there's enough space to build it. 188106163Sroberto */ 189106163Sroberto for (l = strlen(cmd), i = 0; i < nargs; i++) 190290000Sglebius l += strlen(argv[i+1]); 191290000Sglebius if (l > clen && (c = realloc(c, clen = l)) == NULL) 192290000Sglebius err(1, NULL); 193290000Sglebius 194290000Sglebius /* Expand command argv references. */ 195106163Sroberto for (p = cmd, q = c; *p != '\0'; ++p) 196106163Sroberto if (p[0] == magic && isdigit(p[1]) && p[1] != '0') { 197106163Sroberto offset = snprintf(q, l, "%s", 198106163Sroberto argv[(++p)[0] - '0']); 199106163Sroberto if ((size_t)offset >= l) 200106163Sroberto err(1, "snprintf() failed"); 201106163Sroberto q += offset; 202106163Sroberto l -= offset; 203106163Sroberto } else 204106163Sroberto *q++ = *p; 205106163Sroberto 206106163Sroberto /* Terminate the command string. */ 207106163Sroberto *q = '\0'; 208106163Sroberto 209106163Sroberto /* Run the command. */ 210106163Sroberto if (debug) 211106163Sroberto (void)printf("%s\n", c); 212106163Sroberto else 213106163Sroberto if (exec_shell(c, shell, name)) 214290000Sglebius rval = 1; 215106163Sroberto } 216290000Sglebius 217106163Sroberto if (argc != 1) 218106163Sroberto errx(1, "expecting additional argument%s after \"%s\"", 219106163Sroberto (nargs - argc) ? "s" : "", argv[argc - 1]); 220106163Sroberto free(cmd); 221106163Sroberto free(c); 222106163Sroberto free(shell); 223106163Sroberto exit(rval); 224106163Sroberto} 225106163Sroberto 226106163Sroberto/* 227106163Sroberto * exec_shell -- 228106424Sroberto * Execute a shell command using passed use_shell and use_name 229106163Sroberto * arguments. 230106163Sroberto */ 231106163Srobertostatic int 232106163Srobertoexec_shell(const char *command, char *use_shell, char *use_name) 233106163Sroberto{ 234106163Sroberto pid_t pid; 235106163Sroberto int omask, pstat; 236106163Sroberto sig_t intsave, quitsave; 237106163Sroberto 238106163Sroberto if (!command) /* just checking... */ 239106163Sroberto return(1); 240106163Sroberto 241106163Sroberto omask = sigblock(sigmask(SIGCHLD)); 242106163Sroberto switch(pid = vfork()) { 243106163Sroberto case -1: /* error */ 244106163Sroberto err(1, "vfork"); 245106163Sroberto case 0: /* child */ 246106163Sroberto (void)sigsetmask(omask); 247106163Sroberto execl(use_shell, use_name, "-c", command, NULL); 248106163Sroberto warn("%s", use_shell); 249106163Sroberto _exit(1); 250106163Sroberto } 251106163Sroberto intsave = signal(SIGINT, SIG_IGN); 252106163Sroberto quitsave = signal(SIGQUIT, SIG_IGN); 253106163Sroberto pid = waitpid(pid, &pstat, 0); 254106163Sroberto (void)sigsetmask(omask); 255106163Sroberto (void)signal(SIGINT, intsave); 256106163Sroberto (void)signal(SIGQUIT, quitsave); 257106163Sroberto return(pid == -1 ? -1 : pstat); 258106163Sroberto} 259106163Sroberto 260106163Srobertovoid 261106163Srobertousage() 262106163Sroberto{ 263106163Sroberto 264106163Sroberto (void)fprintf(stderr, 265106163Sroberto "usage: apply [-a magic] [-d] [-0123456789] command arguments ...\n"); 266106163Sroberto exit(1); 267106163Sroberto} 268106163Sroberto