morpheus-base

morpheus base system
git clone git://git.2f30.org/morpheus-base.git
Log | Files | Refs

xargs.c (4212B)


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