summaryrefslogtreecommitdiff
path: root/util/sbase/xargs.c
diff options
context:
space:
mode:
authorLeah Rowe <leah@libreboot.org>2025-10-04 09:14:33 +0100
committerLeah Rowe <leah@libreboot.org>2025-10-04 09:20:12 +0100
commite9a910b33c7837b4b868e3abda18eb4810df7f02 (patch)
tree749e1830cb0607952df1a1afc0ae09ec1db54140 /util/sbase/xargs.c
parent2cfaba181b3c68761871fa47b32725c934423c14 (diff)
config/git: import suckless sbase
i currently use the output of sha512sum in several places of xbmk, which is a bit unreliable in case output changes. other cases where i use util outputs in variables are probably reliable, because i'm using mostly posix utilities in those. to mitigate this, i now import suckless sbase, which has a reasonable sha512sum implementation. *every* binary it builds is being placed in build.list, because i'll probably start using more of them. for example, i may start modifying the "date" implementation, adding the GNU-specific options that i need as mentioned on init.sh i'm importing it in util/ because the sha512sum util is needed for verifying project sources, so if sbase itself is a "project source", that means we can into a chicken and egg bootstrapping problem. this is sbase at revision: 055cc1ae1b3a13c3d8f25af0a4a3316590efcd48 Signed-off-by: Leah Rowe <leah@libreboot.org>
Diffstat (limited to 'util/sbase/xargs.c')
-rw-r--r--util/sbase/xargs.c362
1 files changed, 362 insertions, 0 deletions
diff --git a/util/sbase/xargs.c b/util/sbase/xargs.c
new file mode 100644
index 00000000..b3b2a81c
--- /dev/null
+++ b/util/sbase/xargs.c
@@ -0,0 +1,362 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#define NARGS 10000
+
+static int inputc(void);
+static void fillargbuf(int);
+static int eatspace(void);
+static int parsequote(int);
+static int parseescape(void);
+static char *poparg(void);
+static void waitchld(int);
+static void spawn(void);
+
+static size_t argbsz;
+static size_t argbpos;
+static size_t maxargs;
+static size_t curprocs, maxprocs = 1;
+static int nerrors;
+static int nulflag, nflag, pflag, rflag, tflag, xflag, Iflag;
+static char *argb;
+static char *cmd[NARGS];
+static char *eofstr;
+
+static int
+inputc(void)
+{
+ int ch;
+
+ ch = getc(stdin);
+ if (ch == EOF && ferror(stdin))
+ eprintf("getc <stdin>:");
+
+ return ch;
+}
+
+static void
+fillargbuf(int ch)
+{
+ if (argbpos >= argbsz) {
+ argbsz = argbpos == 0 ? 1 : argbsz * 2;
+ argb = erealloc(argb, argbsz);
+ }
+ argb[argbpos] = ch;
+}
+
+static int
+eatspace(void)
+{
+ int ch;
+
+ while ((ch = inputc()) != EOF) {
+ if (nulflag || !(ch == ' ' || ch == '\t' || ch == '\n')) {
+ ungetc(ch, stdin);
+ return ch;
+ }
+ }
+ return -1;
+}
+
+static int
+parsequote(int q)
+{
+ int ch;
+
+ while ((ch = inputc()) != EOF) {
+ if (ch == q)
+ return 0;
+ if (ch != '\n') {
+ fillargbuf(ch);
+ argbpos++;
+ }
+ }
+
+ return -1;
+}
+
+static int
+parseescape(void)
+{
+ int ch;
+
+ if ((ch = inputc()) != EOF) {
+ fillargbuf(ch);
+ argbpos++;
+ return ch;
+ }
+
+ return -1;
+}
+
+static char *
+poparg(void)
+{
+ int ch;
+
+ argbpos = 0;
+ if (eatspace() < 0)
+ return NULL;
+ while ((ch = inputc()) != EOF) {
+ /* NUL separator: no escaping */
+ if (nulflag) {
+ if (ch == '\0')
+ goto out;
+ else
+ goto fill;
+ }
+
+ switch (ch) {
+ case ' ':
+ case '\t':
+ if (Iflag)
+ goto fill;
+ case '\n':
+ goto out;
+ case '\'':
+ if (parsequote('\'') < 0)
+ eprintf("unterminated single quote\n");
+ break;
+ case '\"':
+ if (parsequote('\"') < 0)
+ eprintf("unterminated double quote\n");
+ break;
+ case '\\':
+ if (parseescape() < 0)
+ eprintf("backslash at EOF\n");
+ break;
+ default:
+ fill:
+ fillargbuf(ch);
+ argbpos++;
+ break;
+ }
+ }
+out:
+ fillargbuf('\0');
+
+ return (eofstr && !strcmp(argb, eofstr)) ? NULL : argb;
+}
+
+static void
+waitchld(int waitall)
+{
+ pid_t pid;
+ int status;
+
+ while ((pid = waitpid(-1, &status, !waitall && curprocs < maxprocs ?
+ WNOHANG : 0)) > 0) {
+ curprocs--;
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) == 255)
+ exit(124);
+ if (WEXITSTATUS(status) == 127 ||
+ WEXITSTATUS(status) == 126)
+ exit(WEXITSTATUS(status));
+ if (WEXITSTATUS(status))
+ nerrors++;
+ }
+ if (WIFSIGNALED(status))
+ exit(125);
+ }
+ if (pid == -1 && errno != ECHILD)
+ eprintf("waitpid:");
+}
+
+static int
+prompt(void)
+{
+ FILE *fp;
+ int ch, ret;
+
+ if (!(fp = fopen("/dev/tty", "r")))
+ return -1;
+
+ fputs("?...", stderr);
+ fflush(stderr);
+
+ ch = fgetc(fp);
+ ret = (ch == 'y' || ch == 'Y');
+ if (ch != EOF && ch != '\n') {
+ while ((ch = fgetc(fp)) != EOF) {
+ if (ch == '\n')
+ break;
+ }
+ }
+
+ fclose(fp);
+
+ return ret;
+}
+
+static void
+spawn(void)
+{
+ int savederrno;
+ int first = 1;
+ char **p;
+
+ if (pflag || tflag) {
+ for (p = cmd; *p; p++) {
+ if (!first)
+ fputc(' ', stderr);
+ fputs(*p, stderr);
+ first = 0;
+ }
+ if (pflag) {
+ switch (prompt()) {
+ case -1: break; /* error */
+ case 0: return; /* no */
+ case 1: goto dospawn; /* yes */
+ }
+ }
+ fputc('\n', stderr);
+ fflush(stderr);
+ }
+
+dospawn:
+ switch (fork()) {
+ case -1:
+ eprintf("fork:");
+ case 0:
+ execvp(*cmd, cmd);
+ savederrno = errno;
+ weprintf("execvp %s:", *cmd);
+ _exit(126 + (savederrno == ENOENT));
+ }
+ curprocs++;
+ waitchld(0);
+}
+
+static void
+usage(void)
+{
+ eprintf("usage: %s [-0prtx] [-E eofstr] [-n num] [-P maxprocs] [-s num] "
+ "[cmd [arg ...]]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ret = 0, leftover = 0, i, j;
+ size_t argsz, argmaxsz;
+ size_t arglen, a;
+ char *arg = "";
+ char *replstr;
+
+ if ((argmaxsz = sysconf(_SC_ARG_MAX)) == (size_t)-1)
+ argmaxsz = _POSIX_ARG_MAX;
+ /* Leave some room for environment variables */
+ argmaxsz -= 4096;
+
+ ARGBEGIN {
+ case '0':
+ nulflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ maxargs = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 's':
+ argmaxsz = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'x':
+ xflag = 1;
+ break;
+ case 'E':
+ eofstr = EARGF(usage());
+ break;
+ case 'I':
+ Iflag = 1;
+ xflag = 1;
+ nflag = 1;
+ maxargs = 1;
+ replstr = EARGF(usage());
+ break;
+ case 'P':
+ maxprocs = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
+ break;
+ default:
+ usage();
+ } ARGEND
+
+ do {
+ argsz = 0; i = 0; a = 0;
+ if (argc) {
+ for (; i < argc; i++) {
+ cmd[i] = estrdup(argv[i]);
+ argsz += strlen(cmd[i]) + 1;
+ }
+ } else {
+ cmd[i] = estrdup("/bin/echo");
+ argsz += strlen("/bin/echo") + 1;
+ i++;
+ }
+ while (leftover || (arg = poparg())) {
+ arglen = strlen(arg);
+ if (argsz + arglen >= argmaxsz || i >= NARGS - 1) {
+ if (xflag || arglen >= argmaxsz || leftover)
+ eprintf("insufficient argument space\n");
+ leftover = 1;
+ break;
+ }
+
+ if (!Iflag) {
+ cmd[i] = estrdup(arg);
+ argsz += arglen + 1;
+ } else {
+ for (j = 1; j < i; j++) {
+ char *p = cmd[j];
+ argsz -= strlen(cmd[j]);
+ strnsubst(&cmd[j], replstr, arg, 255);
+ argsz += strlen(cmd[j]);
+ free(p);
+ }
+ }
+
+ i++;
+ a++;
+ leftover = 0;
+ if (nflag && a >= maxargs)
+ break;
+ }
+ cmd[i] = NULL;
+ if (a >= maxargs && nflag)
+ spawn();
+ else if (!a || (i == 1 && rflag))
+ ;
+ else
+ spawn();
+ for (; i >= 0; i--)
+ free(cmd[i]);
+ } while (arg);
+
+ free(argb);
+
+ waitchld(1);
+
+ if (nerrors || (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>")))
+ ret = 123;
+
+ return ret;
+}