commit 86ed3d35616d58be660771325d65f2e1e3abb0ca
parent 7f468e9155e4ea68b86dc56e85be7f29e832633c
Author: Henry Wilson <henry@henryandlizzy.uk>
Date: Fri, 26 Aug 2022 23:42:53 +0100
enviro: Run program with environment variables set
Diffstat:
A | src/enviro.c | | | 134 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 134 insertions(+), 0 deletions(-)
diff --git a/src/enviro.c b/src/enviro.c
@@ -0,0 +1,134 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <regex.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+
+__attribute__((noreturn))
+void die(char const* msg)
+{
+ fprintf(stderr, "enviro: %s\n", msg);
+ exit(1);
+}
+
+__attribute__((noreturn))
+void errorno_die(char const* msg)
+{
+ perror(msg);
+ exit(1);
+}
+
+__attribute__((noreturn))
+void regex_die(int e, regex_t const* r, char const* msg)
+{
+ size_t size = regerror(e, r, NULL, 0);
+ char* buf = malloc(size);
+
+ regerror(e, r, buf, size);
+ fprintf(stderr, "%s: %s\n", msg, buf);
+ exit(1);
+}
+
+int regexec_or_die(const regex_t *restrict preg, const char *restrict string, size_t nmatch, regmatch_t pmatch[restrict], int eflags)
+{
+ int e = regexec(preg, string, nmatch, pmatch, eflags);
+ switch (e)
+ {
+ case REG_NOMATCH:
+ case 0:
+ return e;
+
+ default:
+ regex_die(e, preg, "regexec");
+ }
+}
+
+char const usage[] =
+ "USAGE:\n"
+ "\tenviro (file | -) command [args...]\n"
+ "\nSUMMARY:\n"
+ "\tRun command with the environment from a file or stdin\n";
+
+char const* regexes[] =
+{
+ "^[[:blank:]]*(#.*)?\n$",
+ "^[[:blank:]]*([[:alnum:]_.,]+=.*)\n$",
+};
+
+regex_t r[ARRAY_SIZE(regexes)];
+
+int main(int argc, char* argv[])
+{
+ FILE* f = stdin;
+
+ for (unsigned i = 0; i < ARRAY_SIZE(r); ++i)
+ {
+ int e = regcomp(r + i, regexes[i], REG_EXTENDED | REG_NEWLINE);
+ if (e)
+ regex_die(e, r + i, "regcomp");
+ }
+
+ if (argc < 3)
+ {
+ fputs(usage, stderr);
+ return 1;
+ }
+
+ if (strcmp("-", argv[1]))
+ {
+ struct stat s;
+ int fd = open(argv[1], O_RDONLY);
+
+ if (fd < 0)
+ errorno_die("open");
+
+ if (fstat(fd, &s))
+ errorno_die("fstat");
+
+ if ((s.st_mode & S_IFMT) == S_IFDIR)
+ {
+ fprintf(stderr, "enviro: '%s' is a directory\n", argv[1]);
+ exit(1);
+ }
+
+ f = fdopen(fd, "r");
+ if (!f)
+ errorno_die("fdopen");
+ }
+
+ clearenv();
+
+ for (;;)
+ {
+ char* line = NULL;
+ size_t size = 0;
+ regmatch_t m[2];
+
+ if (getline(&line, &size, f) == -1)
+ break;
+
+ if (!regexec_or_die(&r[0], line, 0, NULL, 0))
+ continue;
+
+ if (regexec_or_die(r+1, line, ARRAY_SIZE(m), m, 0))
+ {
+ fprintf(stderr, "enviro: bad format: %s\n", line);
+ return 1;
+ }
+
+ if (m[1].rm_so == -1)
+ die("bad match");
+
+ line[m[1].rm_eo] = '\0';
+
+ if (putenv(line + m[1].rm_so))
+ errorno_die("putenv");
+ }
+
+ execvp(argv[2], argv + 2);
+ errorno_die("execvp");
+}