sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

xargs.c (4934B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <sys/wait.h>
      3 
      4 #include <errno.h>
      5 #include <limits.h>
      6 #include <stdint.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <unistd.h>
     11 
     12 #include "util.h"
     13 
     14 #define NARGS 10000
     15 
     16 static int inputc(void);
     17 static void fillargbuf(int);
     18 static int eatspace(void);
     19 static int parsequote(int);
     20 static int parseescape(void);
     21 static char *poparg(void);
     22 static void waitchld(void);
     23 static void spawn(void);
     24 
     25 static size_t argbsz;
     26 static size_t argbpos;
     27 static size_t maxargs = 0;
     28 static int    nerrors = 0;
     29 static int    rflag = 0, nflag = 0, tflag = 0, xflag = 0, Iflag = 0;
     30 static char  *argb;
     31 static char  *cmd[NARGS];
     32 static char  *eofstr;
     33 
     34 static int
     35 inputc(void)
     36 {
     37 	int ch;
     38 
     39 	ch = getc(stdin);
     40 	if (ch == EOF && ferror(stdin))
     41 		eprintf("getc <stdin>:");
     42 
     43 	return ch;
     44 }
     45 
     46 static void
     47 fillargbuf(int ch)
     48 {
     49 	if (argbpos >= argbsz) {
     50 		argbsz = argbpos == 0 ? 1 : argbsz * 2;
     51 		argb = erealloc(argb, argbsz);
     52 	}
     53 	argb[argbpos] = ch;
     54 }
     55 
     56 static int
     57 eatspace(void)
     58 {
     59 	int ch;
     60 
     61 	while ((ch = inputc()) != EOF) {
     62 		switch (ch) {
     63 		case ' ': case '\t': case '\n':
     64 			break;
     65 		default:
     66 			ungetc(ch, stdin);
     67 			return ch;
     68 		}
     69 	}
     70 	return -1;
     71 }
     72 
     73 static int
     74 parsequote(int q)
     75 {
     76 	int ch;
     77 
     78 	while ((ch = inputc()) != EOF) {
     79 		if (ch == q)
     80 			return 0;
     81 		if (ch != '\n') {
     82 			fillargbuf(ch);
     83 			argbpos++;
     84 		}
     85 	}
     86 
     87 	return -1;
     88 }
     89 
     90 static int
     91 parseescape(void)
     92 {
     93 	int ch;
     94 
     95 	if ((ch = inputc()) != EOF) {
     96 		fillargbuf(ch);
     97 		argbpos++;
     98 		return ch;
     99 	}
    100 
    101 	return -1;
    102 }
    103 
    104 static char *
    105 poparg(void)
    106 {
    107 	int ch;
    108 
    109 	argbpos = 0;
    110 	if (eatspace() < 0)
    111 		return NULL;
    112 	while ((ch = inputc()) != EOF) {
    113 		switch (ch) {
    114 		case ' ':
    115 		case '\t':
    116 			if (Iflag)
    117 				goto fill;
    118 		case '\n':
    119 			goto out;
    120 		case '\'':
    121 			if (parsequote('\'') < 0)
    122 				eprintf("unterminated single quote\n");
    123 			break;
    124 		case '\"':
    125 			if (parsequote('\"') < 0)
    126 				eprintf("unterminated double quote\n");
    127 			break;
    128 		case '\\':
    129 			if (parseescape() < 0)
    130 				eprintf("backslash at EOF\n");
    131 			break;
    132 		default:
    133 		fill:
    134 			fillargbuf(ch);
    135 			argbpos++;
    136 			break;
    137 		}
    138 	}
    139 out:
    140 	fillargbuf('\0');
    141 
    142 	return (eofstr && !strcmp(argb, eofstr)) ? NULL : argb;
    143 }
    144 
    145 static void
    146 waitchld(void)
    147 {
    148 	int status;
    149 
    150 	wait(&status);
    151 	if (WIFEXITED(status)) {
    152 		if (WEXITSTATUS(status) == 255)
    153 			exit(124);
    154 		if (WEXITSTATUS(status) == 127 ||
    155 		    WEXITSTATUS(status) == 126)
    156 			exit(WEXITSTATUS(status));
    157 		if (status)
    158 			nerrors++;
    159 	}
    160 	if (WIFSIGNALED(status))
    161 		exit(125);
    162 }
    163 
    164 static void
    165 spawn(void)
    166 {
    167 	int savederrno;
    168 	int first = 1;
    169 	char **p;
    170 
    171 	if (tflag) {
    172 		for (p = cmd; *p; p++) {
    173 			if (!first)
    174 				fputc(' ', stderr);
    175 			fputs(*p, stderr);
    176 			first = 0;
    177 		}
    178 		fputc('\n', stderr);
    179 	}
    180 
    181 	switch (fork()) {
    182 	case -1:
    183 		eprintf("fork:");
    184 	case 0:
    185 		execvp(*cmd, cmd);
    186 		savederrno = errno;
    187 		weprintf("execvp %s:", *cmd);
    188 		_exit(126 + (savederrno == ENOENT));
    189 	}
    190 	waitchld();
    191 }
    192 
    193 static void
    194 usage(void)
    195 {
    196 	eprintf("usage: %s [-rtx] [-E eofstr] [-n num] [-s num] "
    197 	        "[cmd [arg ...]]\n", argv0);
    198 }
    199 
    200 int
    201 main(int argc, char *argv[])
    202 {
    203 	int ret = 0, leftover = 0, i, j;
    204 	size_t argsz, argmaxsz;
    205 	size_t arglen, a;
    206 	char *arg = "";
    207 	char *replstr;
    208 
    209 	if ((argmaxsz = sysconf(_SC_ARG_MAX)) == (size_t)-1)
    210 		argmaxsz = _POSIX_ARG_MAX;
    211 	/* Leave some room for environment variables */
    212 	argmaxsz -= 4096;
    213 
    214 	ARGBEGIN {
    215 	case 'n':
    216 		nflag = 1;
    217 		maxargs = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
    218 		break;
    219 	case 'r':
    220 		rflag = 1;
    221 		break;
    222 	case 's':
    223 		argmaxsz = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
    224 		break;
    225 	case 't':
    226 		tflag = 1;
    227 		break;
    228 	case 'x':
    229 		xflag = 1;
    230 		break;
    231 	case 'E':
    232 		eofstr = EARGF(usage());
    233 		break;
    234 	case 'I':
    235 		Iflag = 1;
    236 		xflag = 1;
    237 		nflag = 1;
    238 		maxargs = 1;
    239 		replstr = EARGF(usage());
    240 		break;
    241 	default:
    242 		usage();
    243 	} ARGEND
    244 
    245 	do {
    246 		argsz = 0; i = 0; a = 0;
    247 		if (argc) {
    248 			for (; i < argc; i++) {
    249 				cmd[i] = estrdup(argv[i]);
    250 				argsz += strlen(cmd[i]) + 1;
    251 			}
    252 		} else {
    253 			cmd[i] = estrdup("/bin/echo");
    254 			argsz += strlen("/bin/echo") + 1;
    255 			i++;
    256 		}
    257 		while (leftover || (arg = poparg())) {
    258 			arglen = strlen(arg);
    259 			if (argsz + arglen >= argmaxsz || i >= NARGS - 1) {
    260 				if (arglen >= argmaxsz) {
    261 					weprintf("insufficient argument space\n");
    262 					if (xflag)
    263 						exit(1);
    264 				}
    265 				leftover = 1;
    266 				break;
    267 			}
    268 
    269 			if (!Iflag) {
    270 				cmd[i] = estrdup(arg);
    271 				argsz += arglen + 1;
    272 			} else {
    273 				for (j = 1; j < i; j++) {
    274 					char *p = cmd[j];
    275 					argsz -= strlen(cmd[j]);
    276 					strnsubst(&cmd[j], replstr, arg, 255);
    277 					argsz += strlen(cmd[j]);
    278 					free(p);
    279 				}
    280 			}
    281 
    282 			i++;
    283 			a++;
    284 			leftover = 0;
    285 			if (nflag && a >= maxargs)
    286 				break;
    287 		}
    288 		cmd[i] = NULL;
    289 		if (a >= maxargs && nflag)
    290 			spawn();
    291 		else if (!a || (i == 1 && rflag))
    292 			;
    293 		else
    294 			spawn();
    295 		for (; i >= 0; i--)
    296 			free(cmd[i]);
    297 	} while (arg);
    298 
    299 	free(argb);
    300 
    301 	if (nerrors || (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>")))
    302 		ret = 123;
    303 
    304 	return ret;
    305 }