sbase

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

xargs.c (4546B)


      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;
     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 ' ': case '\t': case '\n':
    115 			goto out;
    116 		case '\'':
    117 			if (parsequote('\'') < 0)
    118 				eprintf("unterminated single quote\n");
    119 			break;
    120 		case '\"':
    121 			if (parsequote('\"') < 0)
    122 				eprintf("unterminated double quote\n");
    123 			break;
    124 		case '\\':
    125 			if (parseescape() < 0)
    126 				eprintf("backslash at EOF\n");
    127 			break;
    128 		default:
    129 			fillargbuf(ch);
    130 			argbpos++;
    131 			break;
    132 		}
    133 	}
    134 out:
    135 	fillargbuf('\0');
    136 
    137 	return (eofstr && !strcmp(argb, eofstr)) ? NULL : argb;
    138 }
    139 
    140 static void
    141 waitchld(void)
    142 {
    143 	int status;
    144 
    145 	wait(&status);
    146 	if (WIFEXITED(status)) {
    147 		if (WEXITSTATUS(status) == 255)
    148 			exit(124);
    149 		if (WEXITSTATUS(status) == 127 ||
    150 		    WEXITSTATUS(status) == 126)
    151 			exit(WEXITSTATUS(status));
    152 		if (status)
    153 			nerrors++;
    154 	}
    155 	if (WIFSIGNALED(status))
    156 		exit(125);
    157 }
    158 
    159 static void
    160 spawn(void)
    161 {
    162 	int savederrno;
    163 	int first = 1;
    164 	char **p;
    165 
    166 	if (tflag) {
    167 		for (p = cmd; *p; p++) {
    168 			if (!first)
    169 				fputc(' ', stderr);
    170 			fputs(*p, stderr);
    171 			first = 0;
    172 		}
    173 		fputc('\n', stderr);
    174 	}
    175 
    176 	switch (fork()) {
    177 	case -1:
    178 		eprintf("fork:");
    179 	case 0:
    180 		execvp(*cmd, cmd);
    181 		savederrno = errno;
    182 		weprintf("execvp %s:", *cmd);
    183 		_exit(126 + (savederrno == ENOENT));
    184 	}
    185 	waitchld();
    186 }
    187 
    188 static void
    189 usage(void)
    190 {
    191 	eprintf("usage: %s [-rtx] [-E eofstr] [-n num] [-s num] "
    192 	        "[cmd [arg ...]]\n", argv0);
    193 }
    194 
    195 int
    196 main(int argc, char *argv[])
    197 {
    198 	int ret = 0, leftover = 0, i;
    199 	size_t argsz, argmaxsz;
    200 	size_t arglen, a;
    201 	char *arg = "";
    202 
    203 	if ((argmaxsz = sysconf(_SC_ARG_MAX)) == (size_t)-1)
    204 		argmaxsz = _POSIX_ARG_MAX;
    205 	/* Leave some room for environment variables */
    206 	argmaxsz -= 4096;
    207 
    208 	ARGBEGIN {
    209 	case 'n':
    210 		nflag = 1;
    211 		maxargs = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
    212 		break;
    213 	case 'r':
    214 		rflag = 1;
    215 		break;
    216 	case 's':
    217 		argmaxsz = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
    218 		break;
    219 	case 't':
    220 		tflag = 1;
    221 		break;
    222 	case 'x':
    223 		xflag = 1;
    224 		break;
    225 	case 'E':
    226 		eofstr = EARGF(usage());
    227 		break;
    228 	default:
    229 		usage();
    230 	} ARGEND
    231 
    232 	do {
    233 		argsz = 0; i = 0; a = 0;
    234 		if (argc) {
    235 			for (; i < argc; i++) {
    236 				cmd[i] = estrdup(argv[i]);
    237 				argsz += strlen(cmd[i]) + 1;
    238 			}
    239 		} else {
    240 			cmd[i] = estrdup("/bin/echo");
    241 			argsz += strlen("/bin/echo") + 1;
    242 			i++;
    243 		}
    244 		while (leftover || (arg = poparg())) {
    245 			arglen = strlen(arg);
    246 			if (argsz + arglen >= argmaxsz || i >= NARGS - 1) {
    247 				if (arglen >= argmaxsz) {
    248 					weprintf("insufficient argument space\n");
    249 					if (xflag)
    250 						exit(1);
    251 				}
    252 				leftover = 1;
    253 				break;
    254 			}
    255 			cmd[i] = estrdup(arg);
    256 			argsz += arglen + 1;
    257 			i++;
    258 			a++;
    259 			leftover = 0;
    260 			if (nflag && a >= maxargs)
    261 				break;
    262 		}
    263 		cmd[i] = NULL;
    264 		if (a >= maxargs && nflag)
    265 			spawn();
    266 		else if (!a || (i == 1 && rflag))
    267 			;
    268 		else
    269 			spawn();
    270 		for (; i >= 0; i--)
    271 			free(cmd[i]);
    272 	} while (arg);
    273 
    274 	free(argb);
    275 
    276 	if (nerrors || (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>")))
    277 		ret = 123;
    278 
    279 	return ret;
    280 }