diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2014-09-18 20:03:23 +0000 |
---|---|---|
committer | Laurent Bercot <ska-skaware@skarnet.org> | 2014-09-18 20:03:23 +0000 |
commit | f316a2ed52195135a35e32d7096e876357c48c69 (patch) | |
tree | 5f4486b9a5a213a69e66ef574d6bc643a207981c /src | |
download | execline-f316a2ed52195135a35e32d7096e876357c48c69.tar.xz |
initial commit: rc for execline-2.0.0.0
Diffstat (limited to 'src')
106 files changed, 3541 insertions, 0 deletions
diff --git a/src/execline/background.c b/src/execline/background.c new file mode 100644 index 0000000..d3e96ff --- /dev/null +++ b/src/execline/background.c @@ -0,0 +1,76 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <unistd.h> +#ifdef EXECLINE_OLD_VARNAMES +#include <skalibs/bytestr.h> +#endif +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> +#include <skalibs/uint64.h> +#include <execline/execline.h> + +#define USAGE "background [ -d ] { command... }" + +int main (int argc, char const **argv, char const *const *envp) +{ + pid_t pid ; + int argc1 ; + int df = 0 ; + PROG = "background" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "d", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'd' : df = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + argc1 = el_semicolon(argv) ; + if (!argc1) strerr_dief1x(100, "empty block") ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + if (argc1 + 1 == argc) df = 0 ; + argv[argc1] = 0 ; + + if (df) + { + pid = doublefork() ; + switch (pid) + { + case -1: strerr_diefu1sys(111, "doublefork") ; + case 0: + PROG = "background (grandchild)" ; + pathexec0_run(argv, envp) ; + strerr_dieexec(127, argv[0]) ; + } + } + else + { + pid = el_spawn0(argv[0], argv, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ; + } + if (argc1 + 1 == argc) return 0 ; + { +#ifdef EXECLINE_OLD_VARNAMES + char fmt[UINT64_FMT * 2 + 10] = "!=" ; +#else + char fmt[UINT64_FMT + 2] = "!=" ; +#endif + register unsigned int i = 2 ; + i += uint64_fmt(fmt+i, (uint64)pid) ; fmt[i++] = 0 ; +#ifdef EXECLINE_OLD_VARNAMES + byte_copy(fmt+i, 8, "LASTPID=") ; i += 8 ; + i += uint64_fmt(fmt+i, (uint64)pid) ; fmt[i++] = 0 ; +#endif + pathexec_r(argv + argc1 + 1, envp, env_len(envp), fmt, i) ; + } + strerr_dieexec(111, argv[argc1+1]) ; +} diff --git a/src/execline/backtick.c b/src/execline/backtick.c new file mode 100644 index 0000000..d0f74e4 --- /dev/null +++ b/src/execline/backtick.c @@ -0,0 +1,84 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <unistd.h> +#include <skalibs/bytestr.h> +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/djbunix.h> +#include <execline/execline.h> + +#define USAGE "backtick [ -i ] [ -n ] var { prog... } remainder..." +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const **argv, char const *const *envp) +{ + subgetopt_t localopt = SUBGETOPT_ZERO ; + int argc1 ; + stralloc modif = STRALLOC_ZERO ; + int insist = 0, chomp = 0 ; + PROG = "backtick" ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "ein", &localopt) ; + if (opt < 0) break ; + switch (opt) + { + case 'i' : insist = 1 ; break ; + case 'n' : chomp = 1 ; break ; + case 'e' : break ; /* compat */ + default : dieusage() ; + } + } + argc -= localopt.ind ; argv += localopt.ind ; + + if (argc < 2) dieusage() ; + if (!*argv[0]) strerr_dief1x(100, "empty variable not accepted") ; + if (!stralloc_cats(&modif, argv[0]) || !stralloc_catb(&modif, "=", 1)) + strerr_diefu1sys(111, "stralloc_catb") ; + argc-- ; argv++ ; + argc1 = el_semicolon(argv) ; + if (!argc1) strerr_dief1x(100, "empty block") ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + + { + int p[2] ; + pid_t pid ; + if (pipe(p) < 0) strerr_diefu1sys(111, "pipe") ; + pid = fork() ; + switch (pid) + { + case -1: strerr_diefu1sys(111, "fork") ; + case 0: + argv[argc1] = 0 ; + fd_close(p[0]) ; + PROG = "backtick (child)" ; + if (fd_move(1, p[1]) < 0) strerr_diefu1sys(111, "fd_move") ; + pathexec_run(argv[0], argv, envp) ; + strerr_dieexec(111, argv[0]) ; + } + fd_close(p[1]) ; + if (!slurp(&modif, p[0])) strerr_diefu1sys(111, "slurp") ; + fd_close(p[0]) ; + if (wait_pid(pid, &p[0]) < 0) strerr_diefu1sys(111, "wait_pid") ; + if (insist && wait_status(p[0])) + strerr_dief1x(wait_status(p[0]), "child process exited non-zero") ; + } + if (argc == argc1 - 1) return 0 ; + if (!stralloc_0(&modif)) strerr_diefu1sys(111, "stralloc_catb") ; + { + unsigned int reallen = str_len(modif.s) ; + if (reallen < modif.len - 1) + { + if (insist) + strerr_dief1x(1, "child process output contained a null character") ; + else + modif.len = reallen + 1 ; + } + if (chomp && (modif.s[modif.len - 2] == '\n')) + modif.s[--modif.len - 1] = 0 ; + } + pathexec_r(argv + argc1 + 1, envp, env_len(envp), modif.s, modif.len) ; + strerr_dieexec(111, argv[argc1 + 1]) ; +} diff --git a/src/execline/cd.c b/src/execline/cd.c new file mode 100644 index 0000000..c774ce5 --- /dev/null +++ b/src/execline/cd.c @@ -0,0 +1,17 @@ +/* ISC license. */ + +#include <unistd.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> + +#define USAGE "cd path prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + PROG = "cd" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + if (chdir(argv[1]) == -1) + strerr_diefu2sys(111, "chdir to ", argv[1]) ; + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/define.c b/src/execline/define.c new file mode 100644 index 0000000..f87c531 --- /dev/null +++ b/src/execline/define.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include <skalibs/strerr2.h> +#include "exlsn.h" + +#define USAGE "define [ -n ] [ -s ] [ -C | -c ] [ -d delim ] key value prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + PROG = "define" ; + exlsn_main(argc, argv, envp, &exlsn_define, USAGE) ; +} diff --git a/src/execline/deps-exe/background b/src/execline/deps-exe/background new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/background @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/backtick b/src/execline/deps-exe/backtick new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/backtick @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/cd b/src/execline/deps-exe/cd new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/cd @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/define b/src/execline/deps-exe/define new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/define @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/dollarat b/src/execline/deps-exe/dollarat new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/dollarat @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/elgetopt b/src/execline/deps-exe/elgetopt new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/elgetopt @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/elgetpositionals b/src/execline/deps-exe/elgetpositionals new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/elgetpositionals @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/elglob b/src/execline/deps-exe/elglob new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/elglob @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/emptyenv b/src/execline/deps-exe/emptyenv new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/emptyenv @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/exec b/src/execline/deps-exe/exec new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/exec @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/execlineb b/src/execline/deps-exe/execlineb new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/execlineb @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/exit b/src/execline/deps-exe/exit new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/exit @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/export b/src/execline/deps-exe/export new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/export @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/fdblock b/src/execline/deps-exe/fdblock new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/fdblock @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/fdclose b/src/execline/deps-exe/fdclose new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/fdclose @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/fdmove b/src/execline/deps-exe/fdmove new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/fdmove @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/fdreserve b/src/execline/deps-exe/fdreserve new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/fdreserve @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/forbacktickx b/src/execline/deps-exe/forbacktickx new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/forbacktickx @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/foreground b/src/execline/deps-exe/foreground new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/foreground @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/forx b/src/execline/deps-exe/forx new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/forx @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/getpid b/src/execline/deps-exe/getpid new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/getpid @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/heredoc b/src/execline/deps-exe/heredoc new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/heredoc @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/homeof b/src/execline/deps-exe/homeof new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/homeof @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/if b/src/execline/deps-exe/if new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/if @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/ifelse b/src/execline/deps-exe/ifelse new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/ifelse @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/ifte b/src/execline/deps-exe/ifte new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/ifte @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/ifthenelse b/src/execline/deps-exe/ifthenelse new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/ifthenelse @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/import b/src/execline/deps-exe/import new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/import @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/importas b/src/execline/deps-exe/importas new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/importas @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/loopwhilex b/src/execline/deps-exe/loopwhilex new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/loopwhilex @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/multidefine b/src/execline/deps-exe/multidefine new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/multidefine @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/multisubstitute b/src/execline/deps-exe/multisubstitute new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/multisubstitute @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/pipeline b/src/execline/deps-exe/pipeline new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/pipeline @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/piperw b/src/execline/deps-exe/piperw new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/piperw @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/redirfd b/src/execline/deps-exe/redirfd new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/redirfd @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/runblock b/src/execline/deps-exe/runblock new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/runblock @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/shift b/src/execline/deps-exe/shift new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/shift @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/tryexec b/src/execline/deps-exe/tryexec new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/tryexec @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/umask b/src/execline/deps-exe/umask new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/umask @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/unexport b/src/execline/deps-exe/unexport new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/unexport @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/wait b/src/execline/deps-exe/wait new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/wait @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/dollarat.c b/src/execline/dollarat.c new file mode 100644 index 0000000..66b5c61 --- /dev/null +++ b/src/execline/dollarat.c @@ -0,0 +1,63 @@ +/* ISC license. */ + +#include <skalibs/bytestr.h> +#include <skalibs/sgetopt.h> +#include <skalibs/buffer.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <skalibs/uint.h> +#include <skalibs/netstring.h> + +#define USAGE "dollarat [ -n ] [ -0 | -d delimchar ]" + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int n, i = 0 ; + char const *x ; + char delim = '\n' ; + int zero = 0 ; + int nl = 1 ; + PROG = "dollarat" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "nd:0", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : nl = 0 ; break ; + case 'd' : delim = *l.arg ; break ; + case '0' : zero = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (zero) delim = 0 ; + x = env_get2(envp, "#") ; + if (!x) strerr_dienotset(100, "#") ; + if (!uint0_scan(x, &n)) strerr_dieinvalid(100, "#") ; + for (; i < n ; i++) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, i+1)] = 0 ; + x = env_get2(envp, fmt) ; + if (!x) strerr_dienotset(100, fmt) ; + if (delim || zero) + { + if ((buffer_puts(buffer_1, x) < 0) + || (((i < n-1) || nl) && (buffer_put(buffer_1, &delim, 1) < 0))) + strerr_diefu1sys(111, "write to stdout") ; + } + else + { + unsigned int written = 0 ; + if (!netstring_put(buffer_1, x, str_len(x), &written)) + strerr_diefu1sys(111, "write a netstring to stdout") ; + } + } + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; + return 0 ; +} diff --git a/src/execline/elgetopt.c b/src/execline/elgetopt.c new file mode 100644 index 0000000..4dfd4d8 --- /dev/null +++ b/src/execline/elgetopt.c @@ -0,0 +1,69 @@ +/* ISC license. */ + +#include <skalibs/bytestr.h> +#include <skalibs/sgetopt.h> +#include <skalibs/env.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <skalibs/skamisc.h> +#include <skalibs/uint.h> +#include <execline/execline.h> + +#define USAGE "elgetopt optstring prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int n, nbak ; + unsigned int envlen = env_len(envp) ; + stralloc modif = STRALLOC_ZERO ; + char const *x = env_get2(envp, "#") ; + PROG = "elgetopt" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + if (!x) strerr_dienotset(100, "#") ; + if (!uint0_scan(x, &n)) strerr_dieinvalid(100, "#") ; + nbak = n++ ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + char const *args[n+1] ; + register unsigned int i = 0 ; + for ( ; i < n ; i++) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, i)] = 0 ; + args[i] = env_get2(envp, fmt) ; + if (!args[i]) strerr_dienotset(100, fmt) ; + } + args[n] = 0 ; + for (;;) + { + char hmpf[11] = "ELGETOPT_?" ; + register int opt = sgetopt_r(n, args, argv[1], &l) ; + if (opt == -1) break ; + if (opt == '?') return 1 ; + hmpf[9] = opt ; + if (!env_addmodif(&modif, hmpf, l.arg ? l.arg : "1")) goto err ; + } + n -= l.ind ; + for (i = 0 ; i < nbak ; i++) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, i+1)] = 0 ; + if (!env_addmodif(&modif, fmt, (i < n) ? args[l.ind + i] : 0)) goto err ; + } + } + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, n)] = 0 ; + if (!env_addmodif(&modif, "#", fmt)) goto err ; + } + { + char const *const list[1] = { "ELGETOPT_" } ; + char const *v[envlen] ; + if (el_pushenv(&satmp, envp, envlen, list, 1) < 0) goto err ; + if (!env_make(v, envlen, satmp.s, satmp.len)) goto err ; + pathexec_r(argv+2, v, envlen, modif.s, modif.len) ; + strerr_dieexec(111, argv[2]) ; + } +err: + strerr_diefu1sys(111, "update environment") ; +} diff --git a/src/execline/elgetpositionals.c b/src/execline/elgetpositionals.c new file mode 100644 index 0000000..3ce4b69 --- /dev/null +++ b/src/execline/elgetpositionals.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include <skalibs/strerr2.h> +#include "exlsn.h" + +#define USAGE "elgetpositionals [ -P num ] prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + PROG = "elgetpositionals" ; + exlsn_main(argc, argv, envp, &exlsn_exlp, USAGE) ; +} diff --git a/src/execline/elglob.c b/src/execline/elglob.c new file mode 100644 index 0000000..ffff74a --- /dev/null +++ b/src/execline/elglob.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include <skalibs/strerr2.h> +#include "exlsn.h" + +#define USAGE "elglob [ -v ] [ -w ] [ -s ] [ -m ] [ -e ] [ -0 ] key pattern prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + PROG = "elglob" ; + exlsn_main(argc, argv, envp, &exlsn_elglob, USAGE) ; +} diff --git a/src/execline/emptyenv.c b/src/execline/emptyenv.c new file mode 100644 index 0000000..cae8869 --- /dev/null +++ b/src/execline/emptyenv.c @@ -0,0 +1,92 @@ +/* ISC license. */ + +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> +#include <execline/execline.h> + +#define USAGE "emptyenv [ -p | -c | -o | -P ] prog..." + +static void cleanupenv (char const *const *argv, char const *const *envp) +{ + stralloc sa = STRALLOC_ZERO ; + if (!pathexec_env("!", 0) || !pathexec_env("?", 0)) goto err ; +#ifdef EXECLINE_OLD_VARNAMES + if (!pathexec_env("LASTPID", 0) || !pathexec_env("LASTEXITCODE", 0)) goto err ; +#endif + for (; *envp ; envp++) + { + char const *s = *envp ; + sa.len = 0 ; + if (!str_diffn(s, "ELGETOPT_", 9) + || !str_diffn(s, "EXECLINE_", 9) + || !str_diffn(s, "FD", 2) + || (s[0] == '#') + || ((s[0] >= '0') && (s[0] <= '9'))) + if (!stralloc_catb(&sa, s, str_chr(s, '=')) + || !stralloc_0(&sa) + || !pathexec_env(sa.s, 0)) + goto err ; + } + stralloc_free(&sa) ; + pathexec(argv) ; + strerr_dieexec(111, argv[0]) ; +err: + strerr_diefu1sys(111, "clean up environment") ; +} + +int main (int argc, char const *const *argv, char const *const *envp) +{ + int flagpath = 0, flagcleanup = 0, flagopt = 0, flagpos = 0 ; + PROG = "emptyenv" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "pcoP", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'p' : flagpath = 1 ; break ; + case 'c' : flagcleanup = 1 ; break ; + case 'o' : flagopt = 1 ; break ; + case 'P' : flagpos = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) strerr_dieusage(100, USAGE) ; + if (flagcleanup) cleanupenv(argv, envp) ; + else if (!flagopt && !flagpos) + { + char const *newenv[2] = { 0, 0 } ; + if (flagpath) + for (; *envp ; envp++) + if (!str_diffn(*envp, "PATH=", 5)) + { + newenv[0] = *envp ; + break ; + } + pathexec_run(argv[0], argv, newenv) ; + } + else + { + static char const *const list[12] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "#", "ELGETOPT_" } ; + stralloc sa = STRALLOC_ZERO ; + unsigned int envlen = env_len(envp) ; + int n = el_popenv(&sa, envp, envlen, flagpos ? list : list + 11, 11 * flagpos + flagopt) ; + if (n < 0) strerr_diefu1sys(111, "pop current execline environment") ; + { + char const *v[envlen - n + 1] ; + if (!env_make(v, envlen-n, sa.s, sa.len)) strerr_diefu1sys(111, "env_make") ; + v[envlen-n] = 0 ; + pathexec_run(argv[0], argv, v) ; + } + stralloc_free(&sa) ; + } + strerr_dieexec(111, argv[0]) ; +} diff --git a/src/execline/exec.c b/src/execline/exec.c new file mode 100644 index 0000000..2213ba4 --- /dev/null +++ b/src/execline/exec.c @@ -0,0 +1,48 @@ +/* ISC license. */ + +#include <skalibs/bytestr.h> +#include <skalibs/djbunix.h> +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> + +#define USAGE "exec [ -c ] [ -l ] [ -a argv0 ] prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + static char const *const zero = 0 ; + char const *executable = 0 ; + char const *argv0 = 0 ; + int dash = 0 ; + PROG = "exec" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "cla:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'c' : envp = &zero ; break ; + case 'l' : dash = 1 ; break ; + case 'a' : argv0 = l.arg ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) strerr_dieusage(100, USAGE) ; + + executable = argv[0] ; + if (argv0) argv[0] = argv0 ; + if (dash) + { + register unsigned int n = str_len(argv[0]) ; + char dashed[n+2] ; + dashed[0] = '-' ; + byte_copy(dashed+1, n+1, argv[0]) ; + argv[0] = (char const *)dashed ; + pathexec_run(executable, argv, envp) ; + } + else pathexec_run(executable, argv, envp) ; + strerr_dieexec(111, executable) ; +} diff --git a/src/execline/execlineb.c b/src/execline/execlineb.c new file mode 100644 index 0000000..1b9e7ad --- /dev/null +++ b/src/execline/execlineb.c @@ -0,0 +1,327 @@ +/* ISC license. */ + +#include <skalibs/uint16.h> +#include <skalibs/uint.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/buffer.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/djbunix.h> +#include <skalibs/skamisc.h> +#include <execline/execline.h> +#include "exlsn.h" + +#define USAGE "execlineb [ -p | -P | -S nmin ] [ -q | -w | -W ] [ -c commandline ] script args" + +typedef unsigned char chargen_t (void) ; + +/* Action (strongest 11 bits) */ + +#define PUSH 0x8000 +#define PUSH0 0x4000 +#define PUSHSPECIAL 0x2000 +#define SETBASE 0x1000 +#define MARK 0x0800 +#define CALC 0x0400 +#define QUOTE 0x0200 +#define INCB 0x0100 +#define DECB 0x0080 + + +/* State (weakest 5 bits) */ + +#define MAIN 0x00 +#define INWORD 0x01 +#define INWORDESC 0x02 +#define INSTR 0x03 +#define INSTRESC 0x04 +#define INREM 0x05 +#define OCT0 0x06 +#define OCT1 0x07 +#define OCT2 0x08 +#define DEC1 0x09 +#define DEC2 0x0a +#define HEX0 0x0b +#define HEX1 0x0c +#define ENDCALC 0x0d +#define OPENB 0x0e +#define CLOSEB 0x0f +#define ERROR 0x10 +#define ACCEPT 0x11 + +static buffer b ; + +static void initbuffer (char const *s) +{ + static char buf[BUFFER_INSIZE] ; + int fd = open_readb(s) ; + if (fd < 0) strerr_diefu3sys(111, "open ", s, " for reading") ; + if (coe(fd) < 0) strerr_diefu2sys(111, "coe ", s) ; + buffer_init(&b, &buffer_read, fd, buf, BUFFER_INSIZE) ; +} + +static unsigned char nextinbuffer () +{ + char c ; + switch (buffer_get(&b, &c, 1)) + { + case -1: strerr_diefu1sys(111, "read script") ; + case 0 : return 0 ; + } + return (unsigned char)c ; +} + +static unsigned char const *string = 0 ; + +static unsigned char nextinstring () +{ + static unsigned int pos = 0 ; + return string[pos++] ; +} + +static int lex (stralloc *sa, chargen_t *next) +{ + static unsigned char const class[256] = "`aaaaaaaaadaaaaaaaaaaaaaaaaaaaaaafcbffffffffffffjhhhhhhhiifffffffmmmmmmfffffffffffffffffffffeffffggmmmgfffffffkfffkfkfkflffnfoffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ; + static uint16 const table[16][16] = + { + { 0x0011, 0x4011, 0x0010, 0x0010, 0x0010, 0x0011, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x4091 }, + { 0x0000, 0x4000, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x0100, 0x4080 }, + { 0x0005, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 }, + { 0x0203, 0x0003, 0x8001, 0x0001, 0x8003, 0x0005, 0x0010, 0x0401, 0x0401, 0x0401, 0x0401, 0x0010, 0x0401, 0x0401, 0x0003, 0x0003 }, + { 0x0000, 0x4000, 0x8001, 0x8003, 0x0003, 0x0000, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x0100, 0x4080 }, + { 0x0202, 0x0002, 0x8001, 0x0004, 0x8003, 0x0005, 0x0010, 0x0404, 0x0404, 0x0404, 0x0404, 0x0010, 0x0404, 0x0404, 0x0002, 0x0002 }, + { 0x8201, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 }, + { 0x8201, 0x8001, 0x8001, 0x8003, 0x2003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 }, + { 0x8201, 0x8001, 0x8001, 0x8003, 0x9809, 0x0005, 0x8807, 0x8008, 0x800d, 0x800a, 0x800d, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 }, + { 0x8201, 0x8001, 0x8001, 0x8003, 0x9809, 0x0005, 0x0010, 0x8403, 0x8403, 0x800a, 0x800d, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 }, + { 0x8201, 0x8001, 0x8001, 0x8003, 0x1006, 0x0005, 0x8807, 0x8008, 0x800d, 0x800a, 0x800d, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 }, + { 0x8201, 0x8001, 0x8001, 0x8003, 0x2003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 }, + { 0x8201, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x100b, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 }, + { 0x8201, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 }, + { 0x820e, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 }, + { 0x820f, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 } + } ; + + unsigned int mark = 0 ; + unsigned int n = 0 ; + unsigned char state = MAIN, base = 10 ; + unsigned int blevel = 0 ; + + while (state < ERROR) + { + unsigned char cur = (*next)() ; + register uint16 c = table[class[cur]-'`'][state] ; + state = c & 0x1F ; + + /* Actions. The order is important ! */ + + if (c & CALC) + { + unsigned int z ; + if (!stralloc_0(sa)) return -1 ; + sa->len = mark ; + uint_scan_base(sa->s + sa->len, &z, base) ; + sa->s[sa->len++] = (unsigned char)z ; + } + if (c & MARK) mark = sa->len ; + if (c & QUOTE) + { + char tilde = EXECLINE_BLOCK_QUOTE_CHAR ; + register unsigned int i = blevel ; + if (!stralloc_readyplus(sa, i<<1)) return -1 ; + while (i--) stralloc_catb(sa, &tilde, 1) ; + } + if (c & INCB) sa->len -= ++blevel ; + if (c & DECB) + { + if (!blevel--) return -4 ; + sa->s[--sa->len-1] = EXECLINE_BLOCK_END_CHAR ; + if (!EXECLINE_BLOCK_END_CHAR) sa->len-- ; + } + if (c & PUSH) if (!stralloc_catb(sa, (char *)&cur, 1)) return -1 ; + if (c & PUSHSPECIAL) + { + char x = 7 + byte_chr("abtnvfr", 7, cur) ; + if (!stralloc_catb(sa, &x, 1)) return -1 ; + } + if (c & PUSH0) if (n++, !stralloc_0(sa)) return -1 ; + if (c & SETBASE) + switch (cur) + { + case 'x' : base = 16 ; break ; + case '0' : base = 8 ; break ; + default : base = 10 ; + } + } + if (state == ERROR) return -2 ; + if (blevel) return -3 ; + return n ; +} + + +static int myexlp (stralloc *sa, char const *const *argv, unsigned int argc, unsigned int nmin, char const *dollar0) +{ + exlsn_t info = EXLSN_ZERO ; + unsigned int n = argc > nmin ? argc : nmin ; + unsigned int i = 0 ; + + if (!genalloc_ready(elsubst_t, &info.data, 3 + n)) return -1 ; + if (!stralloc_ready(&info.vars, 6 + (n << 1))) goto err ; + stralloc_catb(&info.vars, "#\0" "0\0@", 6) ; + { + elsubst_t blah[3] ; + char fmt[UINT_FMT] ; + blah[0].var = 0 ; blah[0].value = 0 ; blah[0].n = 1 ; + if (!stralloc_catb(&info.values, fmt, uint_fmt(fmt, argc)) || !stralloc_0(&info.values)) goto err ; + blah[1].var = 2 ; blah[1].value = info.values.len ; blah[1].n = 1 ; + if (!stralloc_catb(&info.values, dollar0, str_len(dollar0) + 1)) goto err ; + blah[2].var = 4 ; blah[2].value = info.values.len ; blah[2].n = argc ; + genalloc_catb(elsubst_t, &info.data, blah, 3) ; + } + for (; i < n ; i++) + { + elsubst_t blah ; + char fmt[UINT_FMT] ; + blah.var = info.vars.len ; blah.value = info.values.len ; blah.n = 1 ; + if (!stralloc_catb(&info.vars, fmt, uint_fmt(fmt, i+1)) || !stralloc_0(&info.vars)) goto err ; + if (!stralloc_catb(&info.values, i < argc ? argv[i] : "", i < argc ? str_len(argv[i]) + 1 : 1)) goto err ; + genalloc_append(elsubst_t, &info.data, &blah) ; + } + { + stralloc dst = STRALLOC_ZERO ; + int r = el_substitute(&dst, sa->s, sa->len, info.vars.s, info.values.s, genalloc_s(elsubst_t, &info.data), genalloc_len(elsubst_t, &info.data)) ; + if (r < 0) goto err ; + exlsn_free(&info) ; + stralloc_free(sa) ; + *sa = dst ; + return r ; + } + + err: + exlsn_free(&info) ; + return -1 ; +} + + +int main (int argc, char const *const *argv, char const *const *envp) +{ + chargen_t *next ; + stralloc sa = STRALLOC_ZERO ; + stralloc modif = STRALLOC_ZERO ; + int nc ; + int flagstrict = -1 ; + unsigned int nmin = 0 ; + char const *dollar0 = argv[0] ; + unsigned int flagpushenv = 2 ; + PROG = "execlineb" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "pPqwWc:S:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'p' : flagpushenv = 1 ; break ; + case 'P' : flagpushenv = 0 ; break ; + case 'q' : flagstrict = 0 ; break ; + case 'w' : flagstrict = 1 ; break ; + case 'W' : flagstrict = 2 ; break ; + case 'c' : string = (unsigned char *)l.arg ; break ; + case 'S' : + { + if (!uint0_scan(l.arg, &nmin)) strerr_dieusage(100, USAGE) ; + flagpushenv = 3 ; + break ; + } + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (string) next = &nextinstring ; + else + { + if (!argv[0]) strerr_dieusage(100, USAGE) ; + initbuffer(argv[0]) ; + dollar0 = argv[0] ; + argv++ ; argc-- ; + next = &nextinbuffer ; + } + + nc = lex(&sa, next) ; + switch (nc) + { + case -4: strerr_dief2x(100, "unmatched ", "}") ; + case -3: strerr_dief2x(100, "unmatched ", "{") ; + case -2: strerr_dief1x(100, "syntax error") ; + case -1: strerr_diefu1sys(111, "parse script") ; + case 0 : return 0 ; + } + + if (flagstrict >= 0) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, (unsigned int)flagstrict)] = 0 ; + if (!env_addmodif(&modif, "EXECLINE_STRICT", flagstrict ? fmt : 0)) goto errenv ; + } + + if (flagpushenv == 3) + { + flagpushenv = 0 ; + if (flagstrict && ((unsigned int)argc < nmin)) + { + char fmtn[UINT_FMT] ; + char fmta[UINT_FMT] ; + fmtn[uint_fmt(fmtn, nmin)] = 0 ; + fmta[uint_fmt(fmta, argc)] = 0 ; + if (flagstrict > 1) + strerr_dief4x(100, "too few arguments: expecting at least ", fmtn, " but got ", fmta) ; + else + strerr_warnw4x("too few arguments: expecting at least ", fmtn, " but got ", fmta) ; + } + nc = myexlp(&sa, argv, argc, nmin, dollar0) ; + if (nc < 0) strerr_diefu1sys(111, "substitute positional parameters") ; + } + else if (flagpushenv) + { + char fmt[UINT_FMT] ; + register unsigned int i = 0 ; + fmt[uint_fmt(fmt, argc)] = 0 ; + if (!env_addmodif(&modif, "#", fmt)) goto errenv ; + if (!env_addmodif(&modif, "0", dollar0)) goto errenv ; + for (; i < (unsigned int)argc ; i++) + { + fmt[uint_fmt(fmt, i+1)] = 0 ; + if (!env_addmodif(&modif, fmt, argv[i])) goto errenv ; + } + } + + { + char const *v[nc+1] ; + if (!env_make(v, nc, sa.s, sa.len)) strerr_diefu1sys(111, "make argv") ; + v[nc] = 0 ; + + if (flagpushenv > 1) + { + static char const *const list[11] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "#" } ; + unsigned int envlen = env_len(envp) ; + char const *w[envlen] ; + if (el_pushenv(&satmp, envp, envlen, list, 11) < 0) goto errenv ; + if (!env_make(w, envlen, satmp.s, satmp.len)) goto errenv ; + pathexec_r(v, w, envlen, modif.s, modif.len) ; + stralloc_free(&satmp) ; + } + else if (modif.len) + pathexec_r(v, envp, env_len(envp), modif.s, modif.len) ; + else + pathexec_run(v[0], v, envp) ; + } + stralloc_free(&modif) ; + strerr_dieexec(111, sa.s) ; +errenv: + strerr_diefu1sys(111, "update environment") ; +} diff --git a/src/execline/exit.c b/src/execline/exit.c new file mode 100644 index 0000000..d9c6c20 --- /dev/null +++ b/src/execline/exit.c @@ -0,0 +1,15 @@ +/* ISC license. */ + +#include <skalibs/strerr2.h> +#include <skalibs/uint.h> + +#define USAGE "exit [ exitcode ]" + +int main (int argc, char const *const *argv) +{ + unsigned int e ; + PROG = "exit" ; + if (argc < 2) return 0 ; + if (!uint0_scan(argv[1], &e)) strerr_dieusage(100, USAGE) ; + return (int)e ; +} diff --git a/src/execline/export.c b/src/execline/export.c new file mode 100644 index 0000000..e7a7bbe --- /dev/null +++ b/src/execline/export.c @@ -0,0 +1,27 @@ +/* ISC license. */ + +#include <skalibs/bytestr.h> +#include <skalibs/strerr2.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> + +#define USAGE "export variable value prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int len1 ; + PROG = "export" ; + if (argc < 4) strerr_dieusage(100, USAGE) ; + len1 = str_len(argv[1]) ; + if (byte_chr(argv[1], len1, '=') < len1) + strerr_dief2x(100, "invalid variable name: ", argv[1]) ; + { + unsigned int len2 = str_len(argv[2]) ; + char fmt[len1 + len2 + 2] ; + byte_copy(fmt, len1, argv[1]) ; + fmt[len1] = '=' ; + byte_copy(fmt + len1 + 1, len2 + 1, argv[2]) ; + pathexec_r(argv+3, envp, env_len(envp), fmt, len1 + len2 + 2) ; + } + strerr_dieexec(111, argv[3]) ; +} diff --git a/src/execline/fdblock.c b/src/execline/fdblock.c new file mode 100644 index 0000000..486591e --- /dev/null +++ b/src/execline/fdblock.c @@ -0,0 +1,34 @@ +/* ISC license. */ + +#include <skalibs/uint.h> +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> + +#define USAGE "fdblock [ -n ] fd prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int fd ; + int block = 1 ; + PROG = "fdblock" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "n", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : block = 0 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if ((argc < 2) || !uint0_scan(argv[0], &fd)) strerr_dieusage(100, USAGE) ; + if ((block ? ndelay_off(fd) : ndelay_on(fd)) < 0) + strerr_diefu1sys(111, block ? "ndelay_off" : "ndelay_on") ; + pathexec_run(argv[1], argv+1, envp) ; + strerr_dieexec(111, argv[1]) ; +} diff --git a/src/execline/fdclose.c b/src/execline/fdclose.c new file mode 100644 index 0000000..33b5941 --- /dev/null +++ b/src/execline/fdclose.c @@ -0,0 +1,17 @@ +/* ISC license. */ + +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> + +#define USAGE "fdclose fd prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int fd ; + PROG = "fdclose" ; + if ((argc < 3) || !uint0_scan(argv[1], &fd)) strerr_dieusage(100, USAGE) ; + fd_close((int)fd) ; + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/fdmove.c b/src/execline/fdmove.c new file mode 100644 index 0000000..8b7417e --- /dev/null +++ b/src/execline/fdmove.c @@ -0,0 +1,35 @@ +/* ISC license. */ + +#include <skalibs/sgetopt.h> +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> + +#define USAGE "fdmove [ -c ] to from prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int to, from ; + int flagcopy = 0 ; + PROG = "fdmove" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "c", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'c' : flagcopy = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if ((argc < 3) || !uint0_scan(argv[0], &to) || !uint0_scan(argv[1], &from)) + strerr_dieusage(100, USAGE) ; + if ((flagcopy ? fd_copy((int)to, (int)from) : fd_move((int)to, (int)from)) == -1) + strerr_diefu4sys(111, "move fd ", argv[1], " to fd ", argv[0]) ; + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/fdreserve.c b/src/execline/fdreserve.c new file mode 100644 index 0000000..2df721a --- /dev/null +++ b/src/execline/fdreserve.c @@ -0,0 +1,64 @@ +/* ISC license. */ + +#include <unistd.h> +#include <sys/resource.h> +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> + +#define USAGE "fdreserve n prog..." + +#define MAXFDS 1024 + +unsigned int doit (char *modif, unsigned int i, int fd) +{ + register unsigned int pos = 2 ; + modif[0] = 'F' ; modif[1] = 'D' ; + pos += uint_fmt(modif + pos, i) ; + modif[pos++] = '=' ; + pos += uint_fmt(modif + pos, (unsigned int)fd) ; + modif[pos++] = 0 ; + return pos ; +} + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int n ; + PROG = "fdreserve" ; + if ((argc < 3) || !uint0_scan(argv[1], &n)) + strerr_dieusage(100, USAGE) ; + { + struct rlimit lim ; + if (getrlimit(RLIMIT_NOFILE, &lim) < 0) strerr_diefu1sys(111, "getrlimit") ; + if (n > lim.rlim_cur) strerr_dief1x(100, "too many requested fds") ; + } + { + char modif[12 * n] ; /* enough for n times "FDaaaa=bbbb\0" */ + unsigned int j = 0 ; + { + int fd[n >> 1][2] ; + register unsigned int i = 0 ; + for (; i < (n>>1) ; i++) + if (pipe(fd[i]) < 0) + strerr_diefu1sys(111, "reserve fds") ; + if (n & 1) + { + register int lastfd = open_read("/dev/null") ; + if (lastfd < 0) + strerr_diefu1sys(111, "reserve last fd") ; + fd_close(lastfd) ; + j += doit(modif + j, n-1, lastfd) ; + } + for (i = 0 ; i < (n>>1) ; i++) + { + fd_close(fd[i][0]) ; + fd_close(fd[i][1]) ; + j += doit(modif + j, i<<1, fd[i][0]) ; + j += doit(modif + j, (i<<1)|1, fd[i][1]) ; + } + } + pathexec_r(argv+2, envp, env_len(envp), modif, j) ; + } + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/forbacktickx.c b/src/execline/forbacktickx.c new file mode 100644 index 0000000..f7b1460 --- /dev/null +++ b/src/execline/forbacktickx.c @@ -0,0 +1,145 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <errno.h> +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/buffer.h> +#include <skalibs/fmtscan.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> +#include <skalibs/skamisc.h> +#include <skalibs/netstring.h> +#include <skalibs/ushort.h> +#include <execline/config.h> +#include <execline/execline.h> + +#define USAGE "forbacktickx [ -p | -x breakcode,breakcode,... ] [ -n ] [ -C | -c ] [ -0 | -d delim ] var { backtickcmd... } command..." +#define dieusage() strerr_dieusage(100, USAGE) + +static int isbreak (unsigned short *tab, unsigned int n, int code) +{ + register unsigned int i = 0 ; + for (; i < n ; i++) if ((unsigned short)code == tab[i]) break ; + return i < n ; +} + +int main (int argc, char const **argv, char const *const *envp) +{ + genalloc pids = GENALLOC_ZERO ; /* pid_t */ + char const *delim = " \n\r\t" ; + unsigned int delimlen = 4 ; + char const *x ; + int argc1 ; + unsigned short breakcodes[256] ; + unsigned int nbc = 0 ; + int crunch = 0, chomp = 0 ; + PROG = "forbacktickx" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "epnCc0d:x:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'e' : break ; /* compat */ + case 'p' : + { + if (!genalloc_ready(pid_t, &pids, 2)) + strerr_diefu1sys(111, "genalloc_ready") ; + break ; + } + case 'n' : chomp = 1 ; break ; + case 'C' : crunch = 1 ; break ; + case 'c' : crunch = 0 ; break ; + case '0' : delim = "" ; delimlen = 1 ; break ; + case 'd' : delim = l.arg ; delimlen = str_len(delim) ; break ; + case 'x' : + if (!ushort_scanlist(breakcodes, 256, l.arg, &nbc)) dieusage() ; + break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (argc < 2) dieusage() ; + x = argv[0] ; if (!*x) dieusage() ; + argv++ ; argc-- ; + argc1 = el_semicolon(argv) ; + if (!argc1) strerr_dief1x(100, "empty block") ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + argv[argc1] = 0 ; + { + int fd ; + pid_t pidw = el_spawn1(argv[0], argv, envp, &fd, 1) ; + if (!pidw) strerr_diefu2sys(111, "spawn ", argv[0]) ; + { + char buf[BUFFER_INSIZE] ; + buffer b = BUFFER_INIT(&buffer_read, fd, buf, BUFFER_INSIZE) ; + stralloc modif = STRALLOC_ZERO ; + unsigned int envlen = env_len(envp) ; + unsigned int modifstart = str_len(x)+1 ; + char const *newenv[envlen + 2] ; + if (!stralloc_ready(&modif, modifstart+1)) + strerr_diefu1sys(111, "stralloc_ready") ; + byte_copy(modif.s, modifstart-1, x) ; + modif.s[modifstart-1] = '=' ; + for (;;) + { + pid_t pid ; + modif.len = modifstart ; + if (delimlen) + { + register int r = skagetlnsep(&b, &modif, delim, delimlen) ; + if (!r) break ; + else if (r < 0) + { + if (errno != EPIPE) strerr_diefu1sys(111, "skagetlnsep") ; + if (chomp) break ; + } + else modif.len-- ; + if ((modif.len == modifstart) && crunch) continue ; + } + else + { + unsigned int unread = 0 ; + if (netstring_get(&b, &modif, &unread) <= 0) + { + if (netstring_okeof(&b, unread)) break ; + else strerr_diefu1sys(111, "netstring_get") ; + } + } + if (!stralloc_0(&modif)) strerr_diefu1sys(111, "stralloc_0") ; + if (!env_merge(newenv, envlen+2, envp, envlen, modif.s, modif.len)) + strerr_diefu1sys(111, "merge environment") ; + pid = el_spawn0(argv[argc1 + 1], argv + argc1 + 1, newenv) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[argc1+1]) ; + if (pids.s) + { + if (!genalloc_append(pid_t, &pids, &pid)) + strerr_diefu1sys(111, "genalloc_append") ; + } + else + { + int wstat ; + if (wait_pid(pid, &wstat) < 0) + strerr_diefu2sys(111, "wait for ", argv[argc1 + 1]) ; + if (isbreak(breakcodes, nbc, wait_status(wstat))) + return wait_status(wstat) ; + } + } + stralloc_free(&modif) ; + } + fd_close(fd) ; + if (!genalloc_append(pid_t, &pids, &pidw)) + strerr_diefu1sys(111, "genalloc_append") ; + } + if (!waitn(genalloc_s(pid_t, &pids), genalloc_len(pid_t, &pids))) + strerr_diefu1sys(111, "waitn") ; + /* genalloc_free(pid_t, &pids) ; */ + return 0 ; +} diff --git a/src/execline/foreground.c b/src/execline/foreground.c new file mode 100644 index 0000000..8164dcb --- /dev/null +++ b/src/execline/foreground.c @@ -0,0 +1,14 @@ +/* ISC license. */ + +#include <skalibs/strerr2.h> +#include <execline/execline.h> + +int main (int argc, char const **argv, char const *const *envp) +{ + int argc1 ; + PROG = "foreground" ; + argc1 = el_semicolon(++argv) ; + if (argc1 >= --argc) strerr_dief1x(100, "unterminated block") ; + argv[argc1] = 0 ; + el_execsequence(argv, argv+argc1+1, envp) ; +} diff --git a/src/execline/forx.c b/src/execline/forx.c new file mode 100644 index 0000000..25d6d44 --- /dev/null +++ b/src/execline/forx.c @@ -0,0 +1,91 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/strerr2.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> +#include <skalibs/skamisc.h> +#include <skalibs/ushort.h> +#include <execline/config.h> +#include <execline/execline.h> + +#define USAGE "forx [ -p | -x breakcode,breakcode,... ] var { values... } command..." +#define dieusage() strerr_dieusage(100, USAGE) + +static int isbreak (unsigned short *tab, unsigned int n, int code) +{ + register unsigned int i = 0 ; + for (; i < n ; i++) if ((unsigned short)code == tab[i]) break ; + return i < n ; +} + +int main (int argc, char const **argv, char const *const *envp) +{ + char const *x ; + int argc1 ; + unsigned short breakcodes[256] ; + unsigned int nbc = 0 ; + int flagpar = 0 ; + PROG = "forx" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "epx:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'e' : break ; /* compat */ + case 'p' : flagpar = 1 ; break ; + case 'x' : + if (!ushort_scanlist(breakcodes, 256, l.arg, &nbc)) dieusage() ; + break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + if (argc < 2) dieusage() ; + x = argv[0] ; if (!*x) dieusage() ; + argv++ ; argc-- ; + argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + if (!argc1 || (argc1 + 1 == argc)) return 0 ; + { + unsigned int envlen = env_len(envp) ; + unsigned int varlen = str_len(x) ; + unsigned int i = 0 ; + pid_t pids[flagpar ? argc1 : 1] ; + char const *newenv[envlen + 2] ; + + for (; i < (unsigned int)argc1 ; i++) + { + pid_t pid ; + unsigned int vallen = str_len(argv[i]) ; + char modif[varlen + vallen + 2] ; + byte_copy(modif, varlen, x) ; + modif[varlen] = '=' ; + byte_copy(modif + varlen + 1, vallen, argv[i]) ; + modif[varlen + vallen + 1] = 0 ; + if (!env_merge(newenv, envlen + 2, envp, envlen, modif, varlen + vallen + 2)) + strerr_diefu1sys(111, "build new environment") ; + pid = el_spawn0(argv[argc1+1], argv + argc1 + 1, newenv) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[argc1+1]) ; + if (flagpar) pids[i] = pid ; + else + { + int wstat ; + if (wait_pid(pid, &wstat) == -1) + strerr_diefu2sys(111, "wait for ", argv[argc1+1]) ; + if (isbreak(breakcodes, nbc, wait_status(wstat))) + return wait_status(wstat) ; + } + } + if (flagpar) + if (!waitn(pids, argc1)) strerr_diefu1sys(111, "waitn") ; + } + return 0 ; +} diff --git a/src/execline/getpid.c b/src/execline/getpid.c new file mode 100644 index 0000000..03a517c --- /dev/null +++ b/src/execline/getpid.c @@ -0,0 +1,29 @@ +/* ISC license. */ + +#include <unistd.h> +#include <skalibs/bytestr.h> +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> + +#define USAGE "getpid variable prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int len ; + PROG = "getpid" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + len = str_len(argv[1]) ; + if (byte_chr(argv[1], len, '=') < len) + strerr_dief2x(100, "invalid variable name: ", argv[1]) ; + { + char fmt[UINT_FMT + len + 2] ; + unsigned int i = len+1 ; + byte_copy(fmt, len, argv[1]) ; + fmt[len] = '=' ; + i += uint_fmt(fmt+i, getpid()) ; fmt[i++] = 0 ; + pathexec_r(argv+2, envp, env_len(envp), fmt, i) ; + } + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/heredoc.c b/src/execline/heredoc.c new file mode 100644 index 0000000..899c2d8 --- /dev/null +++ b/src/execline/heredoc.c @@ -0,0 +1,59 @@ +/* ISC license. */ + +#include <unistd.h> +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/uint.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> + +#define USAGE "heredoc [ -d ] fd string command..." +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv, char const *const *envp) +{ + int df = 0 ; + PROG = "heredoc" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "d", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'd' : df = 1 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (argc < 3) dieusage() ; + { + int fd[2] ; + unsigned int fdr ; + int pid ; + if (!uint0_scan(argv[0], &fdr)) strerr_dieusage(100, USAGE) ; + if (pipe(fd) < 0) strerr_diefu1sys(111, "pipe") ; + pid = df ? doublefork() : fork() ; + switch (pid) + { + case -1: strerr_diefu2sys(111, df ? "double" : "", "fork") ; + case 0: + { + unsigned int len = str_len(argv[1]) ; + PROG = "heredoc (child)" ; + fd_close(fd[0]) ; + if (allwrite(fd[1], argv[1], len) < len) + strerr_diefu1sys(111, "allwrite") ; + return 0 ; + } + } + fd_close(fd[1]) ; + if (fd_move((int)fdr, fd[0]) == -1) + strerr_diefu2sys(111, "read on fd ", argv[0]) ; + } + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/homeof.c b/src/execline/homeof.c new file mode 100644 index 0000000..40463da --- /dev/null +++ b/src/execline/homeof.c @@ -0,0 +1,28 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <pwd.h> +#include <errno.h> +#include <skalibs/buffer.h> +#include <skalibs/strerr2.h> + +#define USAGE "homeof user" + +int main (int argc, char const *const *argv) +{ + struct passwd *pw ; + PROG = "homeof" ; + if (argc < 2) strerr_dieusage(100, USAGE) ; + pw = getpwnam(argv[1]) ; + if (!pw) + { + if (errno) + strerr_diefu2sys(111, "get passwd entry for ", argv[1]) ; + else + strerr_diefu3x(111, "get passwd entry for ", argv[1], ": no such user") ; + } + if ((buffer_puts(buffer_1small, pw->pw_dir) < 0) + || (buffer_putflush(buffer_1small, "\n", 1) < 0)) + strerr_diefu1sys(111, "write to stdout") ; + return 0 ; +} diff --git a/src/execline/if.c b/src/execline/if.c new file mode 100644 index 0000000..9d0b4b4 --- /dev/null +++ b/src/execline/if.c @@ -0,0 +1,54 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <skalibs/sgetopt.h> +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <skalibs/ushort.h> +#include <execline/execline.h> + +#define USAGE "if [ -n ] [ -X ] [ -t | -x exitcode ] { command... }" + +int main (int argc, char const **argv, char const *const *envp) +{ + int argc1, wstat ; + pid_t pid ; + unsigned int not = 0 ; + unsigned short e = 1 ; + int flagnormalcrash = 0 ; + PROG = "if" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "nXtx:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : not = 1 ; break ; + case 'X' : flagnormalcrash = 1 ; break ; + case 't' : e = 0 ; break ; + case 'x' : if (ushort_scan(l.arg, &e)) break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + argv[argc1] = 0 ; + pid = el_spawn0(argv[0], argv, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ; + if (wait_pid(pid, &wstat) == -1) strerr_diefu1sys(111, "wait_pid") ; + if (!flagnormalcrash && WIFSIGNALED(wstat)) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ; + strerr_dief2x(1, "child crashed with signal ", fmt) ; + } + if (not == !wait_status(wstat)) return (int)e ; + pathexec0_run(argv+argc1+1, envp) ; + strerr_dieexec(111, argv[argc1+1]) ; +} diff --git a/src/execline/ifelse.c b/src/execline/ifelse.c new file mode 100644 index 0000000..6d8801e --- /dev/null +++ b/src/execline/ifelse.c @@ -0,0 +1,54 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <skalibs/uint.h> +#include <execline/execline.h> + +#define USAGE "ifelse [ -n ] [ -X ] { command-if } { command-then... }" + +int main (int argc, char const **argv, char const *const *envp) +{ + int argc1, argc2, wstat ; + int not = 0, flagnormalcrash = 0 ; + pid_t pid ; + PROG = "ifelse" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "nX", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : not = 1 ; break ; + case 'X' : flagnormalcrash = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated if block") ; + if (argc1 + 1 == argc) strerr_dief1x(100, "then block required") ; + argc2 = el_semicolon(argv + argc1 + 1) ; + if (argc1 + argc2 + 1 >= argc) strerr_dief1x(100, "unterminated then block") ; + argv[argc1] = 0 ; + pid = el_spawn0(argv[0], argv, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ; + if (wait_pid(pid, &wstat) == -1) + strerr_diefu2sys(111, "wait for ", argv[0]) ; + argv += ++argc1 ; + if (!flagnormalcrash && WIFSIGNALED(wstat)) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, WSTOPSIG(wstat))] = 0 ; + strerr_dief2x(1, "child crashed with signal ", fmt) ; + } + if (not != !wait_status(wstat)) argv[argc2] = 0 ; else argv += argc2+1 ; + pathexec0_run(argv, envp) ; + strerr_dieexec(111, *argv) ; +} diff --git a/src/execline/ifte.c b/src/execline/ifte.c new file mode 100644 index 0000000..3fe021e --- /dev/null +++ b/src/execline/ifte.c @@ -0,0 +1,59 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <skalibs/sgetopt.h> +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <execline/execline.h> + +#define USAGE "ifte [ -X ] [ -n ] { command-then... } { command-else... } command-if..." + +int main (int argc, char const **argv, char const *const *envp) +{ + int argc1, argc2, wstat ; + int not = 0, flagnormalcrash = 0 ; + pid_t pid ; + PROG = "ifte" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "Xn", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'X' : flagnormalcrash = 1 ; break ; + case 'n' : not = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated then block") ; + if (argc1 + 1 == argc) strerr_dief1x(100, "else block required") ; + argc2 = el_semicolon(argv + argc1 + 1) ; + if (argc1 + argc2 + 1 >= argc) strerr_dief1x(100, "unterminated else block") ; + if (argc1 + argc2 + 2 >= argc) strerr_dief1x(100, "empty command-if") ; + + pid = el_spawn0(argv[argc1 + argc2 + 2], argv + argc1 + argc2 + 2, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[argc1 + argc2 + 2]) ; + if (wait_pid(pid, &wstat) == -1) + strerr_diefu2sys(111, "wait for ", argv[argc1 + argc2 + 2]) ; + if (!flagnormalcrash && WIFSIGNALED(wstat)) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ; + strerr_dief2x(1, "child crashed with signal ", fmt) ; + } + if (not != !wait_status(wstat)) argv[argc1] = 0 ; + else + { + argv += argc1 + 1 ; + argv[argc2] = 0 ; + } + pathexec0_run(argv, envp) ; + strerr_dieexec(111, *argv) ; +} diff --git a/src/execline/ifthenelse.c b/src/execline/ifthenelse.c new file mode 100644 index 0000000..f9fb9d7 --- /dev/null +++ b/src/execline/ifthenelse.c @@ -0,0 +1,77 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <skalibs/sgetopt.h> +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <execline/execline.h> + +#define USAGE "ifthenelse [ -X ] { command-if... } { command-then... } { command-else... } [ remainder... ]" + +int main (int argc, char const **argv, char const *const *envp) +{ + int argc1, argc2, argc3, wstat ; + int magicscope = 0, flagnormalcrash = 0 ; + pid_t pid ; + PROG = "ifthenelse" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "Xs", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'X' : flagnormalcrash = 1 ; break ; + case 's' : magicscope = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated if block") ; + if (argc1 + 1 == argc) strerr_dief1x(100, "then block required") ; + argc2 = el_semicolon(argv + argc1 + 1) ; + if (argc1 + argc2 + 1 >= argc) strerr_dief1x(100, "unterminated then block") ; + argc3 = el_semicolon(argv + argc1 + argc2 + 2) ; + if (argc1 + argc2 + argc3 + 2 >= argc) + strerr_dief1x(100, "unterminated else block") ; + + argv[argc1] = 0 ; + pid = fork() ; + pid = el_spawn0(argv[0], argv, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ; + if (wait_pid(pid, &wstat) == -1) + strerr_diefu2sys(111, "wait for ", argv[0]) ; + argv += argc1 + 1 ; + { + char const *const *remainder = argv + argc2 + argc3 + 2 ; + if (!flagnormalcrash && WIFSIGNALED(wstat)) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ; + strerr_dief2x(1, "child crashed with signal ", fmt) ; + } + if (wait_status(wstat)) + { + argv += argc2 + 1 ; + argc2 = argc3 ; + } + if (magicscope) /* undocumented voodoo - dangerous and powerful */ + { + register unsigned int i = 0 ; + for (; remainder[i] ; i++) argv[argc2+i] = remainder[i] ; + argv[argc2+i] = 0 ; + pathexec0_run(argv, envp) ; + strerr_dieexec(111, argv[0]) ; + } + else + { + argv[argc2] = 0 ; + el_execsequence(argv, remainder, envp) ; + } + } +} diff --git a/src/execline/import.c b/src/execline/import.c new file mode 100644 index 0000000..5a95886 --- /dev/null +++ b/src/execline/import.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include <skalibs/strerr2.h> +#include "exlsn.h" + +#define USAGE "import [ -i | -D default ] [ -n ] [ -s ] [ -C | -c ] [ -d delim ] var prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + PROG = "import" ; + exlsn_main(argc, argv, envp, &exlsn_import, USAGE) ; +} diff --git a/src/execline/importas.c b/src/execline/importas.c new file mode 100644 index 0000000..026efce --- /dev/null +++ b/src/execline/importas.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include <skalibs/strerr2.h> +#include "exlsn.h" + +#define USAGE "importas [ -i | -D default ] [ -n ] [ -s ] [ -C | -c ] [ -d delim ] key var prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + PROG = "importas" ; + exlsn_main(argc, argv, envp, &exlsn_importas, USAGE) ; +} diff --git a/src/execline/loopwhilex.c b/src/execline/loopwhilex.c new file mode 100644 index 0000000..5cb6a4e --- /dev/null +++ b/src/execline/loopwhilex.c @@ -0,0 +1,62 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/ushort.h> +#include <skalibs/djbunix.h> +#include <execline/execline.h> + +#define USAGE "loopwhilex [ -n ] [ -x exitcode,exitcode,... ] prog..." +#define dieusage() strerr_dieusage(100, USAGE) + +static int isbreak (unsigned short *tab, unsigned int n, int code) +{ + register unsigned int i = 0 ; + for (; i < n ; i++) if ((unsigned short)code == tab[i]) break ; + return i < n ; +} + +int main (int argc, char const *const *argv, char const *const *envp) +{ + int wstat ; + int not = 0, cont = 1 ; + unsigned short breakcodes[256] ; + unsigned int nbc = 0 ; + PROG = "loopwhilex" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "nx:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : not = 1 ; break ; + case 'x' : + if (!ushort_scanlist(breakcodes, 256, l.arg, &nbc)) dieusage() ; + break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) dieusage() ; + + if (!nbc) + { + breakcodes[0] = 0 ; + nbc = 1 ; + not = !not ; + } + + while (cont) + { + pid_t pid = el_spawn0(argv[0], argv, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ; + if (wait_pid(pid, &wstat) < 0) strerr_diefu1sys(111, "wait_pid") ; + cont = not == isbreak(breakcodes, nbc, wait_status(wstat)) ; + } + return WIFSIGNALED(wstat) ? WTERMSIG(wstat) : 0 ; +} diff --git a/src/execline/multidefine.c b/src/execline/multidefine.c new file mode 100644 index 0000000..9665418 --- /dev/null +++ b/src/execline/multidefine.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include <skalibs/strerr2.h> +#include "exlsn.h" + +#define USAGE "multidefine [ -0 ] [ -r ] [ -n ] [ -C | -c ] [ -d delim ] value { vars... } prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + PROG = "multidefine" ; + exlsn_main(argc, argv, envp, &exlsn_multidefine, USAGE) ; +} diff --git a/src/execline/multisubstitute.c b/src/execline/multisubstitute.c new file mode 100644 index 0000000..acee42f --- /dev/null +++ b/src/execline/multisubstitute.c @@ -0,0 +1,64 @@ +/* ISC license. */ + +#include <skalibs/bytestr.h> +#include <skalibs/strerr2.h> +#include <execline/execline.h> +#include "exlsn.h" + +#define USAGE "see http://skarnet.org/software/execline/multisubstitute.html" + +static char const *const commands[8] = +{ + "define", + "importas", + "import", + "elglob", + "elgetpositionals", + "multidefine", + 0 +} ; + +static exlsnfunc_t *const functions[8] = +{ + &exlsn_define, + &exlsn_importas, + &exlsn_import, + &exlsn_elglob, + &exlsn_exlp, + &exlsn_multidefine, + 0 +} ; + +int main (int argc, char const **argv, char const *const *envp) +{ + exlsn_t info = EXLSN_ZERO ; + int argc1 ; + PROG = "multisubstitute" ; + if (!--argc) strerr_dieusage(100, USAGE) ; + + /* Read a block containing directives */ + argc1 = el_semicolon(++argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + if (argc1 + 1 == argc) strerr_dieusage(100, USAGE) ; + + /* Parse args and update the substitution info */ + while (argc1) + { + int n ; + unsigned int i = 0 ; + for (; commands[i] ; i++) if (!str_diff(*argv, commands[i])) break ; + if (!commands[i]) strerr_dief3x(100, "syntax error: unrecognized", " directive ", *argv) ; + n = (*(functions[i]))(argc1, argv, envp, &info) ; + if (n < 0) switch (n) + { + case -3 : strerr_dief3x(100, "syntax error at", " directive ", commands[i]) ; + case -2 : strerr_dief3x(100, "wrong key for", " directive ", commands[i]) ; + case -1 : strerr_diefu3sys(111, "run", " directive ", commands[i]) ; + default : strerr_dief3x(111, "unknown error with", " directive ", commands[i]) ; + } + argv += n ; argc1 -= n ; argc -= n ; + } + + /* Perform the substitution and exec */ + el_substandrun(argc-1, argv+1, envp, &info) ; +} diff --git a/src/execline/pipeline.c b/src/execline/pipeline.c new file mode 100644 index 0000000..42a230b --- /dev/null +++ b/src/execline/pipeline.c @@ -0,0 +1,85 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <unistd.h> +#ifdef EXECLINE_OLD_VARNAMES +#include <skalibs/bytestr.h> +#endif +#include <skalibs/sgetopt.h> +#include <skalibs/uint64.h> +#include <skalibs/strerr2.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> +#include <execline/execline.h> + +#define USAGE "pipeline [ -d ] [ -r | -w ] { command... } command..." +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const **argv, char const *const *envp) +{ + int df = 0, w = 0 ; + PROG = "pipeline" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "drw", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'd' : df = 1 ; break ; + case 'r' : w = 0 ; break ; + case 'w' : w = 1 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + { + pid_t pid ; + int fd ; + int argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + if (argc1 + 1 == argc) strerr_dief1x(100, "empty remainder") ; + argv[argc1] = 0 ; + if (df) + { + int p[2] ; + if (pipe(p) < 0) strerr_diefu1sys(111, "create pipe") ; + pid = doublefork() ; + switch (pid) + { + case -1: strerr_diefu1sys(111, "doublefork") ; + case 0: + PROG = "pipeline (grandchild)" ; + fd_close(p[w]) ; + if (fd_move(!w, p[!w]) < 0) strerr_diefu1sys(111, "fd_move") ; + pathexec0_run(argv, envp) ; + strerr_dieexec(127, argv[0]) ; + } + fd_close(p[!w]) ; + fd = p[w] ; + } + else + { + pid = el_spawn1(argv[0], argv, envp, &fd, !w) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ; + } + if (fd_move(w, fd) < 0) strerr_diefu1sys(111, "fd_move") ; + { +#ifdef EXECLINE_OLD_VARNAMES + char fmt[UINT64_FMT * 2 + 10] = "!=" ; +#else + char fmt[UINT64_FMT + 2] = "!=" ; +#endif + register unsigned int i = 2 ; + i += uint64_fmt(fmt+i, (uint64)pid) ; fmt[i++] = 0 ; +#ifdef EXECLINE_OLD_VARNAMES + byte_copy(fmt+i, 8, "LASTPID=") ; i += 8 ; + i += uint64_fmt(fmt+i, (uint64)pid) ; fmt[i++] = 0 ; +#endif + pathexec_r(argv + argc1 + 1, envp, env_len(envp), fmt, i) ; + } + strerr_dieexec(111, argv[argc1 + 1]) ; + } +} diff --git a/src/execline/piperw.c b/src/execline/piperw.c new file mode 100644 index 0000000..9d24947 --- /dev/null +++ b/src/execline/piperw.c @@ -0,0 +1,29 @@ +/* ISC license. */ + +#include <unistd.h> +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> + +#define USAGE "piperw fdr fdw prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + int fdr, fdw ; + int p[2] ; + PROG = "piperw" ; + if ((argc < 4) + || !uint0_scan(argv[1], (unsigned int *)&fdr) + || !uint0_scan(argv[2], (unsigned int *)&fdw) + || (fdr == fdw)) + strerr_dieusage(100, USAGE) ; + if (pipe(p) == -1) + strerr_diefu1sys(111, "create pipe") ; + if (p[1] == fdr) p[1] = dup(p[1]) ; + if ((p[1] == -1) + || (fd_move(fdr, p[0]) == -1) + || (fd_move(fdw, p[1]) == -1)) + strerr_diefu1sys(111, "move fds") ; + pathexec_run(argv[3], argv+3, envp) ; + strerr_dieexec(111, argv[3]) ; +} diff --git a/src/execline/redirfd.c b/src/execline/redirfd.c new file mode 100644 index 0000000..d9dca14 --- /dev/null +++ b/src/execline/redirfd.c @@ -0,0 +1,69 @@ +/* ISC license. */ + +#include <fcntl.h> +#include <errno.h> +#include <skalibs/sgetopt.h> +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> + +#define USAGE "redirfd -[ r | w | u | a | c | x ] [ -n ] [ -b ] fd file prog..." +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv, char const *const *envp) +{ + int fd, fd2 ; + unsigned int flags = 0 ; + int what = -1 ; + int changemode = 0 ; + PROG = "redirfd" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "rwuacxnb", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'r' : what = O_RDONLY ; flags &= ~(O_APPEND|O_CREAT|O_TRUNC|O_EXCL) ; break ; + case 'w' : what = O_WRONLY ; flags |= O_CREAT|O_TRUNC ; flags &= ~(O_APPEND|O_EXCL) ; break ; + case 'u' : what = O_RDWR ; flags &= ~(O_APPEND|O_CREAT|O_TRUNC|O_EXCL) ; break ; + case 'a' : what = O_WRONLY ; flags |= O_CREAT|O_APPEND ; flags &= ~(O_TRUNC|O_EXCL) ; break ; + case 'c' : what = O_WRONLY ; flags |= O_APPEND ; flags &= ~(O_CREAT|O_TRUNC|O_EXCL) ; break ; + case 'x' : what = O_WRONLY ; flags |= O_CREAT|O_EXCL ; flags &= ~(O_APPEND|O_TRUNC) ; break ; + case 'n' : flags |= O_NONBLOCK ; break ; + case 'b' : changemode = 1 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if ((argc < 3) || (what == -1)) dieusage() ; + if (!uint0_scan(argv[0], (unsigned int *)&fd)) dieusage() ; + flags |= what ; + fd2 = open3(argv[1], flags, 0666) ; + if ((fd2 == -1) && (what == O_WRONLY) && (errno == ENXIO)) + { + register int e ; + int fdr = open_read(argv[1]) ; + if (fdr == -1) strerr_diefu2sys(111, "open_read ", argv[1]) ; + fd2 = open3(argv[1], flags, 0666) ; + e = errno ; + fd_close(fdr) ; + errno = e ; + } + if (fd2 == -1) strerr_diefu2sys(111, "open ", argv[1]) ; + if (fd_move(fd, fd2) == -1) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, fd2)] = 0 ; + strerr_diefu4sys(111, "move fd ", fmt, " to fd ", argv[0]) ; + } + if (changemode) + { + if (((flags & O_NONBLOCK) ? ndelay_off(fd) : ndelay_on(fd)) < 0) + strerr_diefu1sys(111, "change blocking mode") ; + } + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/runblock.c b/src/execline/runblock.c new file mode 100644 index 0000000..a654023 --- /dev/null +++ b/src/execline/runblock.c @@ -0,0 +1,149 @@ +/* ISC license. */ + +#include <skalibs/sgetopt.h> +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> +#include <skalibs/skamisc.h> +#include <execline/execline.h> + +#define USAGE "runblock [ -P ] [ -n argshift ] [ -r ] n" +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv, char const *const *envp) +{ + genalloc v = GENALLOC_ZERO ; /* array of char const * */ + unsigned int n, sharp ; + unsigned int m = 0 ; + unsigned int strict = el_getstrict() ; + int flagnopop = 0, flagr = 0 ; + PROG = "runblock" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "Prn:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'P' : flagnopop = 1 ; break ; + case 'r' : flagr = 1 ; break ; + case 'n' : if (!uint0_scan(l.arg, &m)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (argc != 1) dieusage() ; + if (!uint0_scan(argv[0], &n) || (!n && !flagr)) dieusage() ; + + { + char const *x = env_get2(envp, "#") ; + if (!x) strerr_dienotset(100, "#") ; + if (!uint0_scan(x, &sharp)) strerr_dieinvalid(100, "#") ; + } + + /* Skip n-1 blocks (n if flagr) */ + { + register unsigned int i = 1 ; + for (; i < n + flagr ; i++) + { + unsigned int base = m ; + for (;;) + { + char const *x ; + char fmt[UINT_FMT] ; + if (++m > sharp) strerr_dief1x(100, "too few arguments") ; + fmt[uint_fmt(fmt, m)] = 0 ; + x = env_get2(envp, fmt) ; + if (!x) strerr_dienotset(100, fmt) ; + if ((x[0] == EXECLINE_BLOCK_END_CHAR) && (!EXECLINE_BLOCK_END_CHAR || !x[1])) break ; + if ((x[0] != EXECLINE_BLOCK_QUOTE_CHAR) && strict) + { + char fmtb[UINT_FMT] ; + char fmti[UINT_FMT] ; + fmti[uint_fmt(fmti, i)] = 0 ; + fmtb[uint_fmt(fmtb, m - base)] = 0 ; + if (strict == 1) + strerr_warnw6x("unquoted positional ", x, " at block ", fmti, " position ", fmtb) ; + else + strerr_dief6x(100, "unquoted positional ", x, " at block ", fmti, " position ", fmtb) ; + } + } + } + } + + if (flagr) /* put remainder envvars into v */ + { + if (++m == sharp) return 0 ; + if (!genalloc_ready(char const *, &v, sharp - m + 2)) + strerr_diefu1sys(111, "genalloc_ready") ; + for (; m <= sharp ; m++) + { + char const *x ; + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, m)] = 0 ; + x = env_get2(envp, fmt) ; + if (!x) strerr_dienotset(100, fmt) ; + genalloc_append(char const *, &v, &x) ; + } + } + else /* put envvars from nth block into v */ + { + unsigned int base = m ; + for (;;) + { + char const *x ; + char fmt[UINT_FMT] ; + if (++m > sharp) strerr_dief1x(100, "too few arguments") ; + fmt[uint_fmt(fmt, m)] = 0 ; + x = env_get2(envp, fmt) ; + if (!x) strerr_dienotset(100, fmt) ; + if ((x[0] == EXECLINE_BLOCK_END_CHAR) && (!EXECLINE_BLOCK_END_CHAR || !x[1])) break ; + if (x[0] != EXECLINE_BLOCK_QUOTE_CHAR) + { + if (strict) + { + char fmtb[UINT_FMT] ; + char fmtn[UINT_FMT] ; + fmtn[uint_fmt(fmtn, n)] = 0 ; + fmtb[uint_fmt(fmtb, m - base)] = 0 ; + if (strict == 1) + strerr_warnw6x("unquoted positional ", x, " at block ", fmtn, " position ", fmtb) ; + else + strerr_dief6x(100, "unquoted positional ", x, " at block ", fmtn, " position ", fmtb) ; + } + } + else x++ ; + if (!genalloc_append(char const *, &v, &x)) strerr_diefu1sys(111, "envalloc_catb") ; + } + } + + { + char const *z = 0 ; + if (!genalloc_append(char const *, &v, &z)) + strerr_diefu1sys(111, "genalloc_append") ; + } + + if (flagnopop) /* exec now */ + pathexec_run(genalloc_s(char const *, &v)[0], genalloc_s(char const *, &v), envp) ; + else /* popenv, then exec */ + { + char const *list[11] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "#" } ; + unsigned int envlen = env_len(envp) ; + register int popped = el_popenv(&satmp, envp, envlen, list, 11) ; + if (popped < 0) strerr_diefu1sys(111, "pop environment") ; + else + { + char const *w[envlen - popped + 1] ; + if (!env_make(w, envlen - popped, satmp.s, satmp.len)) + strerr_diefu1sys(111, "env_make") ; + w[envlen - popped] = 0 ; + pathexec_run(genalloc_s(char const *, &v)[0], genalloc_s(char const *, &v), w) ; + stralloc_free(&satmp) ; + } + } + strerr_dieexec(111, genalloc_s(char const *, &v)[0]) ; +} diff --git a/src/execline/shift.c b/src/execline/shift.c new file mode 100644 index 0000000..b09ba38 --- /dev/null +++ b/src/execline/shift.c @@ -0,0 +1,121 @@ +/* ISC license. */ + +#include <skalibs/sgetopt.h> +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <execline/execline.h> + +#define USAGE "shift [ -n arg# ] [ -b block# ] prog..." +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int strict = el_getstrict() ; + unsigned int b = 0 ; + unsigned int n = 0 ; + unsigned int sharp ; + unsigned int i = 0 ; + PROG = "shift" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "n:b:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : + if (!uint0_scan(l.arg, &n)) dieusage() ; + i = 1 ; + break ; + case 'b' : + if (!uint0_scan(l.arg, &b)) dieusage() ; + i = 1 ; + break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) dieusage() ; + if (i) i = 0 ; else n = 1 ; + { + char const *x = env_get2(envp, "#") ; + if (!x) strerr_dienotset(100, "#") ; + if (!uint0_scan(x, &sharp)) strerr_dieinvalid(100, "#") ; + } + + + /* Shift n args */ + + if (n > sharp) + { + if (strict) + { + char fmtn[UINT_FMT] ; + char fmtsharp[UINT_FMT] ; + fmtn[uint_fmt(fmtn, n)] = 0 ; + fmtsharp[uint_fmt(fmtsharp, sharp)] = 0 ; + if (strict == 1) + strerr_warnwu5x("shift", " ", fmtn, " arguments: got ", fmtsharp) ; + else + strerr_diefu5x(100, "shift", " ", fmtn, " arguments: got ", fmtsharp) ; + } + n = sharp ; + } + + + /* Shift b blocks */ + + for (; i < b ; i++) + { + for (;;) + { + char const *x ; + unsigned int base = n ; + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, ++n)] = 0 ; + if (n > sharp) + { + char fmti[UINT_FMT] ; + fmti[uint_fmt(fmt, i)] = 0 ; + strerr_diefu6x(100, "shift", " block ", fmti, ": too few arguments (", fmt, ")") ; + } + x = env_get2(envp, fmt) ; + if (!x) strerr_dienotset(100, fmt) ; + if ((x[0] == EXECLINE_BLOCK_END_CHAR) && (!EXECLINE_BLOCK_END_CHAR || !x[1])) break ; + if ((x[0] != EXECLINE_BLOCK_QUOTE_CHAR) && strict) + { + char fmti[UINT_FMT] ; + char fmtp[UINT_FMT] ; + fmti[uint_fmt(fmti, i)] = 0 ; + fmtp[uint_fmt(fmtp, n - base)] = 0 ; + if (strict == 1) + strerr_warnw6x("unquoted positional ", x, " at block ", fmti, " position ", fmtp) ; + else + strerr_dief6x(100, "unquoted positional ", x, " at block ", fmti, " position ", fmtp) ; + } + } + } + + + /* n = shift value; modify the env */ + + { + register unsigned int i = 1 ; + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, sharp - n)] = 0 ; + if (!pathexec_env("#", fmt)) strerr_diefu1sys(111, "pathexec_env") ; + for (; i <= sharp ; i++) + { + char fmu[UINT_FMT] ; + fmt[uint_fmt(fmt, i)] = 0 ; + fmu[uint_fmt(fmu, i + n)] = 0 ; + if (!pathexec_env(fmt, i <= (sharp - n) ? env_get2(envp, fmu) : 0)) + strerr_diefu1sys(111, "pathexec_env") ; + } + } + pathexec(argv) ; + strerr_dieexec(111, argv[0]) ; +} diff --git a/src/execline/tryexec.c b/src/execline/tryexec.c new file mode 100644 index 0000000..46d3479 --- /dev/null +++ b/src/execline/tryexec.c @@ -0,0 +1,61 @@ +/* ISC license. */ + +#include <skalibs/bytestr.h> +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <execline/execline.h> + +#define USAGE "tryexec [ -n ] [ -c ] [ -l ] [ -a argv0 ] { command... }" + +int main (int argc, char const **argv, char const *const *envp) +{ + char const *env_zero[1] = { 0 } ; + char const *executable = 0 ; + char const *argv0 = 0 ; + char const **dom = 0 ; + char const **sub = 0 ; + char const *const *dom_envp = envp ; + int not = 0, dash = 0 ; + PROG = "tryexec" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "ncla:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : not = 1 ; break ; + case 'c' : dom_envp = env_zero ; break ; + case 'l' : dash = 1 ; break ; + case 'a' : argv0 = l.arg ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + { + int argc1 = el_semicolon(argv) ; + if (!argc1) strerr_dief1x(100, "empty block") ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + argv[argc1] = 0 ; + dom = argv + not * (argc1 + 1) ; + sub = argv + !not * (argc1 + 1) ; + } + executable = dom[0] ; + if (argv0) dom[0] = argv0 ; + if (dash) + { + register unsigned int n = str_len(dom[0]) ; + char dashed[n+2] ; + dashed[0] = '-' ; + byte_copy(dashed+1, n+1, dom[0]) ; + dom[0] = dashed ; + pathexec_run(executable, dom, dom_envp) ; + } + else pathexec_run(executable, dom, dom_envp) ; + + pathexec0_run(sub, envp) ; + strerr_dieexec(111, sub[0]) ; +} diff --git a/src/execline/umask.c b/src/execline/umask.c new file mode 100644 index 0000000..81217b6 --- /dev/null +++ b/src/execline/umask.c @@ -0,0 +1,20 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> + +#define USAGE "umask value prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int m ; + PROG = "umask" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + if (!uint_oscan(argv[1], &m)) strerr_dieusage(100, USAGE) ; + umask(m) ; + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/unexport.c b/src/execline/unexport.c new file mode 100644 index 0000000..189249c --- /dev/null +++ b/src/execline/unexport.c @@ -0,0 +1,20 @@ +/* ISC license. */ + +#include <skalibs/bytestr.h> +#include <skalibs/strerr2.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> + +#define USAGE "unexport variable prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int len ; + PROG = "unexport" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + len = str_len(argv[1]) ; + if (byte_chr(argv[1], len, '=') < len) + strerr_dief2x(100, "invalid variable name: ", argv[1]) ; + pathexec_r(argv+2, envp, env_len(envp), argv[1], len+1) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/wait.c b/src/execline/wait.c new file mode 100644 index 0000000..4d550c9 --- /dev/null +++ b/src/execline/wait.c @@ -0,0 +1,62 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <skalibs/sgetopt.h> +#include <skalibs/uint.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <execline/execline.h> + +#define USAGE "wait [ -r ] { pids... }" + +static unsigned int waitall (void) +{ + register unsigned int n = 0 ; + int wstat ; + while (wait(&wstat) > 0) n++ ; + return n ; +} + +int main (int argc, char const **argv, char const *const *envp) +{ + int argc1 ; + int flagreap = 0 ; + PROG = "wait" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "r", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'r' : flagreap = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + if (!argc1) flagreap ? wait_reap() : waitall() ; + else + { + pid_t tab[argc1] ; + register unsigned int i = 0 ; + for (; i < (unsigned int)argc1 ; i++) + { + unsigned int pid ; + if (!uint0_scan(argv[i], &pid)) strerr_dieusage(100, USAGE) ; + tab[i] = pid ; + } + if (flagreap) + { + if (waitn_reap(tab, argc1) < 0) + strerr_diefu1sys(111, "waitn_reap") ; + } + else if (!waitn(tab, argc1)) strerr_diefu1sys(111, "waitn") ; + } + pathexec0_run(argv + argc1 + 1, envp) ; + strerr_dieexec(111, argv[argc1 + 1]) ; +} diff --git a/src/include-local/exlsn.h b/src/include-local/exlsn.h new file mode 100644 index 0000000..3665bca --- /dev/null +++ b/src/include-local/exlsn.h @@ -0,0 +1,36 @@ +/* ISC license. */ + +#ifndef EXLSN_H +#define EXLSN_H + +#include <skalibs/gccattributes.h> +#include <skalibs/stralloc.h> + +typedef struct exlsn_s exlsn_t, *exlsn_t_ref ; +struct exlsn_s +{ + stralloc vars ; + stralloc values ; + stralloc data ; /* array of elsubst */ +} ; + +#define EXLSN_ZERO { .vars = STRALLOC_ZERO, .values = STRALLOC_ZERO, .data = STRALLOC_ZERO } + +extern void exlsn_free (exlsn_t *) ; + +typedef int exlsnfunc_t (int, char const **, char const *const *, exlsn_t *) ; +typedef exlsnfunc_t *exlsnfunc_t_ref ; + +extern exlsnfunc_t exlsn_define ; +extern exlsnfunc_t exlsn_importas ; +extern exlsnfunc_t exlsn_import ; +extern exlsnfunc_t exlsn_elglob ; +extern exlsnfunc_t exlsn_exlp ; +extern exlsnfunc_t exlsn_multidefine ; + +extern int exlp (unsigned int, char const *const *, exlsn_t *) ; +extern void el_substandrun (int, char const *const *, char const *const *, exlsn_t *) gccattr_noreturn ; +extern void el_substandrun_str (stralloc *, unsigned int, char const *const *, exlsn_t *) gccattr_noreturn ; +extern void exlsn_main (int, char const **, char const *const *, exlsnfunc_t *, char const *) gccattr_noreturn ; + +#endif diff --git a/src/include/execline/execline.h b/src/include/execline/execline.h new file mode 100644 index 0000000..a5c16ad --- /dev/null +++ b/src/include/execline/execline.h @@ -0,0 +1,67 @@ +/* ISC license. */ + +#ifndef EXECLINE_H +#define EXECLINE_H + +#include <sys/types.h> +#include <skalibs/gccattributes.h> +#include <skalibs/stralloc.h> + +#define EXECLINE_BLOCK_QUOTE_CHAR ' ' +#define EXECLINE_BLOCK_END_CHAR '\0' + + +/* Basics */ + +extern int el_vardupl (char const *, char const *, unsigned int) gccattr_pure ; +extern unsigned int el_getstrict (void) gccattr_const ; +extern void el_obsolescent (void) ; + + +/* Environment shifting */ + +extern int el_pushenv (stralloc *, char const *const *, unsigned int, char const *const *, unsigned int) ; +extern int el_popenv (stralloc *, char const *const *, unsigned int, char const *const *, unsigned int) ; + + +/* Sequence */ + +extern pid_t el_spawn0 (char const *, char const *const *, char const *const *) ; +extern pid_t el_spawn1 (char const *, char const *const *, char const *const *, int *, int) ; +extern void el_execsequence (char const *const *, char const *const *, char const *const *) gccattr_noreturn ; + + +/* Block unquoting */ + +extern int el_semicolon (char const **) ; + + +/* Value transformation */ + +typedef struct eltransforminfo_s eltransforminfo_t, *eltransforminfo_t_ref ; +struct eltransforminfo_s +{ + char const *delim ; + unsigned int crunch : 1 ; + unsigned int chomp : 1 ; + unsigned int split : 1 ; +} ; + +#define ELTRANSFORMINFO_ZERO { .delim = " \n\r\t", .crunch = 0, .chomp = 0, .split = 0 } + +extern int el_transform (stralloc *, unsigned int, eltransforminfo_t const *) ; + + +/* Substitution */ + +typedef struct elsubst_s elsubst_t, *elsubst_t_ref ; +struct elsubst_s +{ + unsigned int var ; + unsigned int value ; + unsigned int n ; +} ; + +extern int el_substitute (stralloc *, char const *, unsigned int, char const *, char const *, elsubst_t const *, unsigned int) ; + +#endif diff --git a/src/libexecline/deps-lib/execline b/src/libexecline/deps-lib/execline new file mode 100755 index 0000000..69d483d --- /dev/null +++ b/src/libexecline/deps-lib/execline @@ -0,0 +1,21 @@ +el_execsequence.o +el_getstrict.o +el_obsolescent.o +el_popenv.o +el_pushenv.o +el_semicolon.o +el_spawn0.o +el_spawn1.o +el_substandrun.o +el_substandrun_str.o +el_substitute.o +el_transform.o +el_vardupl.o +exlsn_define.o +exlsn_elglob.o +exlsn_import.o +exlsn_multidefine.o +exlsn_exlp.o +exlsn_main.o +exlsn_free.o +exlp.o diff --git a/src/libexecline/el_execsequence.c b/src/libexecline/el_execsequence.c new file mode 100644 index 0000000..6b825af --- /dev/null +++ b/src/libexecline/el_execsequence.c @@ -0,0 +1,41 @@ +/* ISC license. */ + +#include <sys/types.h> +#ifdef EXECLINE_OLD_VARNAMES +#include <skalibs/bytestr.h> +#endif +#include <skalibs/djbunix.h> +#include <skalibs/env.h> +#include <skalibs/strerr2.h> +#include <skalibs/uint.h> +#include <execline/execline.h> + +void el_execsequence (char const *const *argv1, char const *const *argv2, char const *const *envp) +{ + if (!argv2[0]) + { + pathexec0_run(argv1, envp) ; + strerr_dieexec(111, argv1[0]) ; + } + else + { + int wstat ; + unsigned int j = 2 ; +#ifdef EXECLINE_OLD_VARNAMES + char fmt[UINT_FMT * 2 + 15] = "?=" ; +#else + char fmt[UINT_FMT + 1] = "?=" ; +#endif + pid_t pid = el_spawn0(argv1[0], argv1, envp) ; + if (!pid) strerr_warnwu2sys("spawn ", argv1[0]) ; + if (wait_pid(pid, &wstat) < 0) + strerr_diefu2sys(111, "wait for ", argv1[0]) ; + j += uint_fmt(fmt + j, wait_status(wstat)) ; fmt[j++] = 0 ; +#ifdef EXECLINE_OLD_VARNAMES + byte_copy(fmt + j, 13, "LASTEXITCODE=") ; j += 13 ; + j += uint_fmt(fmt + j, wait_status(wstat)) ; fmt[j++] = 0 ; +#endif + pathexec_r(argv2, envp, env_len(envp), fmt, j) ; + } + strerr_dieexec(111, argv2[0]) ; +} diff --git a/src/libexecline/el_getstrict.c b/src/libexecline/el_getstrict.c new file mode 100644 index 0000000..091845c --- /dev/null +++ b/src/libexecline/el_getstrict.c @@ -0,0 +1,18 @@ +/* ISC license. */ + +#include <skalibs/env.h> +#include <skalibs/uint.h> +#include <execline/execline.h> + +unsigned int el_getstrict (void) +{ + static unsigned int strict = 0 ; + static int first = 1 ; + if (first) + { + char const *x = env_get("EXECLINE_STRICT") ; + first = 0 ; + if (x) uint0_scan(x, &strict) ; + } + return strict ; +} diff --git a/src/libexecline/el_obsolescent.c b/src/libexecline/el_obsolescent.c new file mode 100644 index 0000000..42b8a11 --- /dev/null +++ b/src/libexecline/el_obsolescent.c @@ -0,0 +1,10 @@ +/* ISC license. */ + +#include <skalibs/strerr2.h> +#include <execline/execline.h> + +void el_obsolescent (void) +{ + if (el_getstrict()) + strerr_warnw3x("this command is marked as obsolescent. Please update your script to use the ", PROG, "x command instead.") ; +} diff --git a/src/libexecline/el_popenv.c b/src/libexecline/el_popenv.c new file mode 100644 index 0000000..3726506 --- /dev/null +++ b/src/libexecline/el_popenv.c @@ -0,0 +1,44 @@ +/* ISC license. */ + +#include <errno.h> +#include <skalibs/bytestr.h> +#include <skalibs/stralloc.h> +#include <skalibs/uint.h> +#include <execline/execline.h> + +int el_popenv (stralloc *sa, char const *const *envp, unsigned int envlen, char const *const *list, unsigned int listlen) +{ + unsigned int i = 0, salen = sa->len, count = 0 ; + for (; i < envlen ; i++) + { + unsigned int equal, colon, n ; + unsigned int j = 0 ; + for (; j < listlen ; j++) if (str_start(envp[i], list[j])) break ; + if (j == listlen) goto copyit ; + j = str_len(list[j]) ; + colon = j + str_chr(envp[i] + j, ':') ; + equal = j + str_chr(envp[i] + j, '=') ; + if (!envp[i][equal]) goto badenv ; + if (colon >= equal) { count++ ; continue ; } + if (colon + 1 + uint_scan(envp[i] + colon + 1, &n) != equal) goto copyit ; + if (!n) goto copyit ; + if (!stralloc_catb(sa, envp[i], colon)) goto err ; + if (n > 1) + { + char fmt[UINT_FMT+1] = ":" ; + n = 1 + uint_fmt(fmt+1, n-1) ; + if (!stralloc_catb(sa, fmt, n)) goto err ; + } + if (!stralloc_catb(sa, envp[i] + equal, str_len(envp[i] + equal) + 1)) goto err ; + continue ; +copyit: + if (!stralloc_catb(sa, envp[i], str_len(envp[i]) + 1)) goto err ; + } + return (int)count ; + +badenv : + errno = EINVAL ; +err: + sa->len = salen ; + return -1 ; +} diff --git a/src/libexecline/el_pushenv.c b/src/libexecline/el_pushenv.c new file mode 100644 index 0000000..9b9608d --- /dev/null +++ b/src/libexecline/el_pushenv.c @@ -0,0 +1,49 @@ +/* ISC license. */ + +#include <errno.h> +#include <skalibs/bytestr.h> +#include <skalibs/stralloc.h> +#include <skalibs/uint.h> +#include <execline/execline.h> + +int el_pushenv (stralloc *sa, char const *const *envp, unsigned int envlen, char const *const *list, unsigned int listlen) +{ + unsigned int i = 0, salen = sa->len, count = 0 ; + for (; i < envlen ; i++) + { + unsigned int equal, colon ; + unsigned int j = 0 ; + for (; j < listlen ; j++) if (str_start(envp[i], list[j])) break ; + if (j == listlen) goto copyit ; + count++ ; + j = str_len(list[j]) ; + colon = j + str_chr(envp[i] + j, ':') ; + equal = j + str_chr(envp[i] + j, '=') ; + if (!envp[i][equal]) goto badenv ; + if (colon >= equal) + { + if (!stralloc_catb(sa, envp[i], equal) + || !stralloc_catb(sa, ":1", 2)) goto err ; + } + else + { + char fmt[UINT_FMT+1] = ":" ; + unsigned int n ; + if (colon + 1 + uint_scan(envp[i] + colon + 1, &n) != equal) goto copyit ; + n = 1 + uint_fmt(fmt+1, n+1) ; + if (!stralloc_catb(sa, envp[i], colon)) goto err ; + if (!stralloc_catb(sa, fmt, n)) goto err ; + } + if (!stralloc_catb(sa, envp[i] + equal, str_len(envp[i] + equal) + 1)) goto err ; + continue ; +copyit: + if (!stralloc_catb(sa, envp[i], str_len(envp[i]) + 1)) goto err ; + } + return (int)count ; + +badenv : + errno = EINVAL ; +err: + sa->len = salen ; + return -1 ; +} diff --git a/src/libexecline/el_semicolon.c b/src/libexecline/el_semicolon.c new file mode 100644 index 0000000..dc99daf --- /dev/null +++ b/src/libexecline/el_semicolon.c @@ -0,0 +1,35 @@ +/* ISC license. */ + +#include <skalibs/env.h> +#include <skalibs/strerr2.h> +#include <skalibs/uint.h> +#include <execline/execline.h> + +int el_semicolon (char const **argv) +{ + static unsigned int nblock = 0 ; + register int argc1 = 0 ; + nblock++ ; + for (;; argc1++, argv++) + { + register char const *arg = *argv ; + if (!arg) return argc1 + 1 ; + if ((arg[0] == EXECLINE_BLOCK_END_CHAR) && (!EXECLINE_BLOCK_END_CHAR || !arg[1])) return argc1 ; + else if (arg[0] == EXECLINE_BLOCK_QUOTE_CHAR) ++*argv ; + else + { + unsigned int strict = el_getstrict() ; + if (strict) + { + char fmt1[UINT_FMT] ; + char fmt2[UINT_FMT] ; + fmt1[uint_fmt(fmt1, nblock)] = 0 ; + fmt2[uint_fmt(fmt2, (unsigned int)argc1)] = 0 ; + if (strict >= 2) + strerr_dief6x(100, "unquoted argument ", arg, " at block ", fmt1, " position ", fmt2) ; + else + strerr_warnw6x("unquoted argument ", arg, " at block ", fmt1, " position ", fmt2) ; + } + } + } +} diff --git a/src/libexecline/el_spawn0.c b/src/libexecline/el_spawn0.c new file mode 100644 index 0000000..4b7f50f --- /dev/null +++ b/src/libexecline/el_spawn0.c @@ -0,0 +1,15 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <skalibs/djbunix.h> +#include <execline/execline.h> + +pid_t el_spawn0 (char const *prog, char const *const *argv, char const *const *envp) +{ + if (!argv[0]) + { + static char const *const newargv[2] = { "/bin/true", 0 } ; + return child_spawn0(newargv[0], newargv, 0) ; + } + else return child_spawn0(prog, argv, envp) ; +} diff --git a/src/libexecline/el_spawn1.c b/src/libexecline/el_spawn1.c new file mode 100644 index 0000000..4785b92 --- /dev/null +++ b/src/libexecline/el_spawn1.c @@ -0,0 +1,15 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <skalibs/djbunix.h> +#include <execline/execline.h> + +pid_t el_spawn1 (char const *prog, char const *const *argv, char const *const *envp, int *fd, int w) +{ + if (!argv[0]) + { + static char const *const newargv[2] = { "/bin/true", 0 } ; + return child_spawn1(newargv[0], newargv, 0, fd, w) ; + } + else return child_spawn1(prog, argv, envp, fd, w) ; +} diff --git a/src/libexecline/el_substandrun.c b/src/libexecline/el_substandrun.c new file mode 100644 index 0000000..7dddbca --- /dev/null +++ b/src/libexecline/el_substandrun.c @@ -0,0 +1,13 @@ +/* ISC license. */ + +#include <skalibs/env.h> +#include <skalibs/strerr2.h> +#include <skalibs/skamisc.h> +#include "exlsn.h" + +void el_substandrun (int argc, char const *const *argv, char const *const *envp, exlsn_t *info) +{ + satmp.len = 0 ; + if (!env_string(&satmp, argv, (unsigned int)argc)) strerr_diefu1sys(111, "env_string") ; + el_substandrun_str(&satmp, 0, envp, info) ; +} diff --git a/src/libexecline/el_substandrun_str.c b/src/libexecline/el_substandrun_str.c new file mode 100644 index 0000000..351ec9d --- /dev/null +++ b/src/libexecline/el_substandrun_str.c @@ -0,0 +1,25 @@ +/* ISC license. */ + +#include <skalibs/djbunix.h> +#include <skalibs/env.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <execline/execline.h> +#include "exlsn.h" + +void el_substandrun_str (stralloc *src, unsigned int srcbase, char const *const *envp, exlsn_t *info) +{ + stralloc dst = STRALLOC_ZERO ; + register int r = el_substitute(&dst, src->s + srcbase, src->len, info->vars.s, info->values.s, genalloc_s(elsubst_t, &info->data), genalloc_len(elsubst_t, &info->data)) ; + if (r < 0) strerr_diefu1sys(111, "el_substitute") ; + exlsn_free(info) ; + stralloc_free(src) ; + { + char const *v[r + 1] ; + if (!env_make(v, r, dst.s, dst.len)) strerr_diefu1sys(111, "env_make") ; + v[r] = 0 ; + pathexec0_run(v, envp) ; + } + strerr_dieexec(111, dst.s) ; +} diff --git a/src/libexecline/el_substitute.c b/src/libexecline/el_substitute.c new file mode 100644 index 0000000..a94c16d --- /dev/null +++ b/src/libexecline/el_substitute.c @@ -0,0 +1,179 @@ +/* ISC license. */ + +#include <skalibs/bytestr.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <execline/execline.h> + +typedef struct elsubsu_s elsubsu_t, *elsubsu_t_ref ; +struct elsubsu_s +{ + elsubst_t const *subst ; + unsigned int pos ; +} ; + +typedef struct subsuinfo_s subsuinfo_t, *subsuinfo_t_ref ; +struct subsuinfo_s +{ + stralloc dst ; + stralloc sa ; + genalloc list ; /* array of elsubsu_t */ + char const *values ; +} ; + +#define SUBSUINFO_ZERO { .dst = STRALLOC_ZERO, .sa = STRALLOC_ZERO, .list = GENALLOC_ZERO, .values = 0 } + +#define TEST 0x80 +#define MARK 0x40 +#define KEEPESC 0x20 +#define INCRESC 0x10 + +#define STATE 0x07 +#define INWORD 0x00 +#define INDOLL 0x01 +#define INDBR 0x02 +#define INVAR 0x03 +#define INVARBR 0x04 +#define ACCEPT 0x05 + +static int parseword (stralloc *sa, genalloc *list, char const *s, char const *vars, elsubst_t const *substs, unsigned int nsubst) +{ + static char const class[5] = "\0\\${}" ; + static unsigned char const table[6][5] = + { + { ACCEPT, ACCEPT, ACCEPT, ACCEPT | TEST, ACCEPT }, + { INWORD | KEEPESC | INCRESC, INWORD | INCRESC, INWORD | INCRESC, INWORD | TEST | INCRESC, INWORD | INCRESC }, + { INDOLL | KEEPESC, INDOLL, INDOLL, INDOLL | TEST, INDOLL }, + { INWORD, INDBR | KEEPESC, INWORD, INWORD | TEST, INWORD }, + { INWORD, INWORD, INWORD, INWORD | TEST, INWORD | TEST }, + { INWORD, INVAR | MARK | KEEPESC, INVARBR | MARK | KEEPESC, INVAR | KEEPESC, INVARBR | KEEPESC } + } ; + + unsigned int mark = 0, pos = 0, offset = 0, esc = 0, salen = sa->len, listlen = genalloc_len(elsubsu_t, list) ; + unsigned char state = INWORD ; + + while (state != ACCEPT) + { + int nopush = 0 ; + unsigned char c = table[byte_chr(class, 5, s[pos])][state] ; + + if (c & TEST) + { + unsigned int supp = (state == INVARBR) ; + unsigned int i = 0 ; + for (; i < nsubst ; i++) + { + if (!str_diffn(vars + substs[i].var, s + mark, pos - mark) && !vars[substs[i].var + pos - mark]) + { + sa->len -= esc >> 1 ; offset += esc >> 1 ; + if (esc & 1) + { + byte_copy(sa->s + mark - offset - 2 - supp, pos - mark + 1 + supp, sa->s + mark - offset + (esc>>1) - 1 - supp) ; + sa->len-- ; offset++ ; + } + else + { + elsubsu_t cur ; + cur.subst = substs + i ; + cur.pos = mark - offset - 1 - supp ; + if (!genalloc_append(elsubsu_t, list, &cur)) goto err ; + offset += sa->len - cur.pos ; + sa->len = cur.pos ; + if (supp) nopush = 1 ; + } + break ; + } + } + } + if (nopush) offset++ ; + else if (!stralloc_catb(sa, s+pos, 1)) goto err ; + if (c & MARK) mark = pos ; + if (!(c & KEEPESC)) esc = 0 ; + if (c & INCRESC) esc++ ; + state = c & STATE ; pos++ ; + } + sa->len-- ; + return (int)pos ; + +err: + sa->len = salen ; + list->len = listlen ; + return -1 ; +} + +static int substword (subsuinfo_t *info, unsigned int wordstart, unsigned int wordlen, unsigned int n, unsigned int offset) +{ + if (n < genalloc_len(elsubsu_t, &info->list)) + { + elsubsu_t *list = genalloc_s(elsubsu_t, &info->list) ; + char const *p = info->values + list[n].subst->value ; + unsigned int l = list[n].pos + offset ; + unsigned int dstbase = info->dst.len ; + unsigned int sabase = info->sa.len ; + unsigned int i = 0 ; + int nc = 0 ; + if (!stralloc_readyplus(&info->sa, l)) return -1 ; + stralloc_catb(&info->sa, info->sa.s + wordstart, l) ; + for ( ; i < list[n].subst->n ; i++) + { + int r ; + unsigned int plen = str_len(p) ; + info->sa.len = sabase + l ; + if (!stralloc_readyplus(&info->sa, plen + wordlen - l)) goto err ; + stralloc_catb(&info->sa, p, plen) ; + stralloc_catb(&info->sa, info->sa.s + wordstart + l, wordlen - l) ; + r = substword(info, sabase, info->sa.len - sabase, n+1, offset + plen) ; + if (r < 0) goto err ; + nc += r ; + p += plen+1 ; + } + return nc ; + err: + info->sa.len = sabase ; + info->dst.len = dstbase ; + return -1 ; + } + else + { + if (!stralloc_readyplus(&info->dst, wordlen+1)) return -1 ; + stralloc_catb(&info->dst, info->sa.s + wordstart, wordlen) ; + stralloc_0(&info->dst) ; + return 1 ; + } +} + +int el_substitute (stralloc *dst, char const *src, unsigned int len, char const *vars, char const *values, elsubst_t const *substs, unsigned int nsubst) +{ + subsuinfo_t info = SUBSUINFO_ZERO ; + unsigned int nc = 0 ; + unsigned int i = 0 ; + unsigned int dstbase = dst->len ; + int wasnull = !dst->s ; + info.dst = *dst ; + info.values = values ; + + while (i < len) + { + int r ; + genalloc_setlen(elsubsu_t, &info.list, 0) ; + info.sa.len = 0 ; + r = parseword(&info.sa, &info.list, src + i, vars, substs, nsubst) ; + if (r < 0) goto err ; + i += r ; + r = substword(&info, 0, info.sa.len, 0, 0) ; + if (r < 0) goto err ; + nc += r ; + } + genalloc_free(elsubsu_t, &info.list) ; + stralloc_free(&info.sa) ; + if (!wasnull) stralloc_free(dst) ; + *dst = info.dst ; + return (int)nc ; + +err : + genalloc_free(elsubsu_t, &info.list) ; + stralloc_free(&info.sa) ; + if (wasnull) stralloc_free(&info.dst) ; else info.dst.len = dstbase ; + *dst = info.dst ; + return -1 ; +} diff --git a/src/libexecline/el_transform.c b/src/libexecline/el_transform.c new file mode 100644 index 0000000..ac84d1d --- /dev/null +++ b/src/libexecline/el_transform.c @@ -0,0 +1,84 @@ +/* ISC license. */ + +#include <skalibs/bytestr.h> +#include <skalibs/netstring.h> +#include <skalibs/skamisc.h> +#include <skalibs/stralloc.h> +#include <execline/execline.h> + +static void el_crunch (stralloc *sa, unsigned int base, char const *delim) +{ + register unsigned int i = base, j = base ; + register int crunching = 0 ; + for (; i < sa->len ; i++) + { + if (!crunching) sa->s[j++] = sa->s[i] ; + if (delim[str_chr(delim, sa->s[i])]) crunching = 1 ; + else if (crunching) + { + i-- ; + crunching = 0 ; + } + } + sa->len = j ; +} + +static int el_split (stralloc *sa, unsigned int base, eltransforminfo_t const *si, int chomped) +{ + unsigned int n = 0 ; + register unsigned int i = base ; + for (; i < sa->len ; i++) + if (si->delim[str_chr(si->delim, sa->s[i])]) + { + sa->s[i] = 0 ; + n++ ; + base = i+1 ; + } + + if (sa->len && sa->s[sa->len - 1]) + { + if (si->chomp && !chomped) sa->len = base ; + else if (!stralloc_0(sa)) return -1 ; + else n++ ; + } + return n ; +} + +static int el_splitnetstring (stralloc *sa, unsigned int base) +{ + unsigned int tmpbase = satmp.len ; + unsigned int n = 0, i = base ; + while (i < sa->len) + { + register int r = netstring_decode(&satmp, sa->s + i, sa->len - i) ; + if (r < 0) goto err ; + if (!stralloc_0(&satmp)) goto err ; + i += r ; n++ ; + } + sa->len = base ; + if (!stralloc_catb(sa, satmp.s + tmpbase, satmp.len - tmpbase)) + { + sa->len = i ; + goto err ; + } + satmp.len = tmpbase ; + return n ; + +err: + satmp.len = tmpbase ; + return -1 ; +} + +int el_transform (stralloc *sa, unsigned int i, eltransforminfo_t const *si) +{ + int chomped = 0 ; + if (si->crunch && *si->delim) el_crunch(sa, i, si->delim) ; + if (si->chomp && (sa->len > i) + && si->delim[str_chr(si->delim, sa->s[sa->len-1])]) + { + sa->len-- ; + chomped = 1 ; + } + return si->split ? *si->delim ? el_split(sa, i, si, chomped) : el_splitnetstring(sa, i) : + stralloc_0(sa) ? 1 : -1 ; +} diff --git a/src/libexecline/el_vardupl.c b/src/libexecline/el_vardupl.c new file mode 100644 index 0000000..7583669 --- /dev/null +++ b/src/libexecline/el_vardupl.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include <skalibs/bytestr.h> +#include <execline/execline.h> + +int el_vardupl (char const *key, char const *s, unsigned int len) +{ + register unsigned int i = 0 ; + for (; i < len ; i += str_len(s + i) + 1) + if (!str_diff(key, s + i)) return 1 ; + return 0 ; +} diff --git a/src/libexecline/exlp.c b/src/libexecline/exlp.c new file mode 100644 index 0000000..060eb68 --- /dev/null +++ b/src/libexecline/exlp.c @@ -0,0 +1,78 @@ +/* ISC license. */ + +#include <skalibs/bytestr.h> +#include <skalibs/env.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/uint.h> +#include <execline/execline.h> +#include "exlsn.h" + +int exlp (unsigned int nmin, char const *const *envp, exlsn_t *info) +{ + unsigned int varbase = info->vars.len ; + unsigned int valbase = info->values.len ; + unsigned int datbase = genalloc_len(elsubst_t, &info->data) ; + unsigned int i = 0 ; + char const *x = env_get2(envp, "#") ; + elsubst_t blah ; + unsigned int n, ntot, poszero ; + if (!x) return -2 ; + if (!uint0_scan(x, &n)) return -2 ; + if (el_vardupl("#", info->vars.s, info->vars.len)) return -2 ; + if (el_vardupl("@", info->vars.s, info->vars.len)) return -2 ; + { + register unsigned int strict = el_getstrict() ; + if (strict && (n < nmin)) + { + char fmta[UINT_FMT] ; + char fmtn[UINT_FMT] ; + fmta[uint_fmt(fmta, n)] = 0 ; + fmtn[uint_fmt(fmtn, nmin)] = 0 ; + if (strict > 1) + strerr_dief4x(100, "too few arguments: expected at least ", fmtn, " but got ", fmta) ; + else + strerr_warnw4x("too few arguments: expected at least ", fmtn, " but got ", fmta) ; + } + } + blah.var = varbase ; + blah.value = info->values.len ; + blah.n = 1 ; + if (!stralloc_catb(&info->vars, "#\0@", 4) + || !stralloc_catb(&info->values, x, str_len(x) + 1) + || !genalloc_append(elsubst_t, &info->data, &blah)) goto err ; + ntot = n > nmin ? n : nmin ; + poszero = info->values.len ; + for (; i <= ntot ; i++) + { + char fmt[UINT_FMT] ; + unsigned int l = uint_fmt(fmt, i) ; + fmt[l] = 0 ; + if (el_vardupl(fmt, info->vars.s, info->vars.len)) goto err2 ; + x = (i <= n) ? env_get2(envp, fmt) : "" ; + if (!x) goto err2 ; + blah.var = info->vars.len ; + blah.value = info->values.len ; + blah.n = 1 ; + if (!stralloc_catb(&info->vars, fmt, l+1) + || !stralloc_catb(&info->values, x, str_len(x) + 1) + || !genalloc_append(elsubst_t, &info->data, &blah)) goto err ; + } + blah.var = varbase + 2 ; + blah.value = poszero + str_len(info->values.s + poszero) + 1 ; + blah.n = n ; + if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ; + return n ; + + err: + info->vars.len = varbase ; + info->values.len = valbase ; + genalloc_setlen(elsubst_t, &info->data, datbase) ; + return -1 ; + err2: + info->vars.len = varbase ; + info->values.len = valbase ; + genalloc_setlen(elsubst_t, &info->data, datbase) ; + return -2 ; +} diff --git a/src/libexecline/exlsn_define.c b/src/libexecline/exlsn_define.c new file mode 100644 index 0000000..3abe516 --- /dev/null +++ b/src/libexecline/exlsn_define.c @@ -0,0 +1,50 @@ +/* ISC license. */ + +#include <skalibs/bytestr.h> +#include <skalibs/sgetopt.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <execline/execline.h> +#include "exlsn.h" + +int exlsn_define (int argc, char const **argv, char const *const *envp, exlsn_t *info) +{ + eltransforminfo_t si = ELTRANSFORMINFO_ZERO ; + subgetopt_t localopt = SUBGETOPT_ZERO ; + elsubst_t blah ; + blah.var = info->vars.len ; + blah.value = info->values.len ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "nsCcd:", &localopt) ; + if (opt < 0) break ; + switch (opt) + { + case 'n' : si.chomp = 1 ; break ; + case 's' : si.split = 1 ; break ; + case 'C' : si.crunch = 1 ; break ; + case 'c' : si.crunch = 0 ; break ; + case 'd' : si.delim = localopt.arg ; break ; + default : return -3 ; + } + } + argc -= localopt.ind ; argv += localopt.ind ; + + if (argc < 2) return -3 ; + if (!*argv[0] || el_vardupl(argv[0], info->vars.s, info->vars.len)) return -2 ; + if (!stralloc_catb(&info->vars, argv[0], str_len(argv[0]) + 1)) return -1 ; + if (!stralloc_cats(&info->values, argv[1])) goto err ; + { + register int r = el_transform(&info->values, blah.value, &si) ; + if (r < 0) goto err ; + blah.n = r ; + } + if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ; + (void)envp ; + return localopt.ind + 2 ; + + err: + info->vars.len = blah.var ; + info->values.len = blah.value ; + return -1 ; +} diff --git a/src/libexecline/exlsn_elglob.c b/src/libexecline/exlsn_elglob.c new file mode 100644 index 0000000..67e939b --- /dev/null +++ b/src/libexecline/exlsn_elglob.c @@ -0,0 +1,78 @@ +/* ISC license. */ + +#include <errno.h> +#include <glob.h> +#include <skalibs/bytestr.h> +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <execline/execline.h> +#include "exlsn.h" + +static int elgloberrfunc (char const *s, int e) +{ + errno = e ; + strerr_warnw2sys("while globbing, error reading ", s) ; + return 0 ; +} + +int exlsn_elglob (int argc, char const **argv, char const *const *envp, exlsn_t *info) +{ + glob_t pglob ; + subgetopt_t localopt = SUBGETOPT_ZERO ; + elsubst_t blah ; + int flags = GLOB_NOSORT | GLOB_NOCHECK ; + unsigned int i = 0 ; + int verbose = 0 ; + blah.var = info->vars.len ; + blah.value = info->values.len ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "vwsme0", &localopt) ; + if (opt < 0) break ; + switch (opt) + { + case 'v' : verbose = 1 ; break ; + case 'w' : flags |= GLOB_ERR ; break ; + case 's' : flags &= ~GLOB_NOSORT ; break ; + case 'm' : flags |= GLOB_MARK ; break ; + case 'e' : flags |= GLOB_NOESCAPE ; break ; + case '0' : flags &= ~GLOB_NOCHECK ; break ; + default : return -3 ; + } + } + argc -= localopt.ind ; argv += localopt.ind ; + + if (argc < 2) return -3 ; + if (!*argv[0] || el_vardupl(argv[0], info->vars.s, info->vars.len)) return -2 ; + if (!stralloc_catb(&info->vars, argv[0], str_len(argv[0]) + 1)) return -1 ; + + pglob.gl_offs = 0 ; + switch (glob(argv[1], flags, verbose ? &elgloberrfunc : 0, &pglob)) + { + case 0 : break ; + case GLOB_NOMATCH: + { + pglob.gl_pathc = 0 ; + pglob.gl_pathv = 0 ; + break ; + } + default: goto err ; + } + for ( ; i < (unsigned int)pglob.gl_pathc ; i++) + if (!stralloc_catb(&info->values, pglob.gl_pathv[i], str_len(pglob.gl_pathv[i]) + 1)) + goto globerr ; + blah.n = pglob.gl_pathc ; + globfree(&pglob) ; + if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ; + (void)envp ; + return localopt.ind + 2 ; + + globerr: + globfree(&pglob) ; + err: + info->vars.len = blah.var ; + info->values.len = blah.value ; + return -1 ; +} diff --git a/src/libexecline/exlsn_exlp.c b/src/libexecline/exlsn_exlp.c new file mode 100644 index 0000000..4ca513e --- /dev/null +++ b/src/libexecline/exlsn_exlp.c @@ -0,0 +1,27 @@ +/* ISC license. */ + +#include <skalibs/sgetopt.h> +#include <skalibs/uint.h> +#include "exlsn.h" + +int exlsn_exlp (int argc, char const **argv, char const *const *envp, exlsn_t *info) +{ + subgetopt_t localopt = SUBGETOPT_ZERO ; + unsigned int nmin = 0 ; + int n ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "P:", &localopt) ; + if (opt < 0) break ; + switch (opt) + { + case 'P' : if (uint0_scan(localopt.arg, &nmin)) break ; + default : return -3 ; + } + } + argc -= localopt.ind ; argv += localopt.ind ; + if (!argc) return -3 ; + n = exlp(nmin, envp, info) ; + if (n < 0) return n ; + return localopt.ind ; +} diff --git a/src/libexecline/exlsn_free.c b/src/libexecline/exlsn_free.c new file mode 100644 index 0000000..4d9dde3 --- /dev/null +++ b/src/libexecline/exlsn_free.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include <skalibs/stralloc.h> +#include "exlsn.h" + +void exlsn_free (exlsn_t *info) +{ + stralloc_free(&info->vars) ; + stralloc_free(&info->values) ; + stralloc_free(&info->data) ; +} + diff --git a/src/libexecline/exlsn_import.c b/src/libexecline/exlsn_import.c new file mode 100644 index 0000000..1574027 --- /dev/null +++ b/src/libexecline/exlsn_import.c @@ -0,0 +1,76 @@ +/* ISC license. */ + +#include <skalibs/bytestr.h> +#include <skalibs/sgetopt.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/env.h> +#include <execline/execline.h> +#include "exlsn.h" + +static int exlsn_import_as (int argc, char const **argv, char const *const *envp, exlsn_t *info, unsigned int as) +{ + eltransforminfo_t si = ELTRANSFORMINFO_ZERO ; + subgetopt_t localopt = SUBGETOPT_ZERO ; + elsubst_t blah ; + char const *defaultval = 0 ; + char const *x ; + int insist = 0 ; + blah.var = info->vars.len ; + blah.value = info->values.len ; + + for (;;) + { + register int opt = subgetopt_r(argc, argv, "iD:nsCcd:", &localopt) ; + if (opt < 0) break ; + switch (opt) + { + case 'i' : insist = 1 ; break ; + case 'D' : defaultval = localopt.arg ; break ; + case 'n' : si.chomp = 1 ; break ; + case 's' : si.split = 1 ; break ; + case 'C' : si.crunch = 1 ; break ; + case 'c' : si.crunch = 0 ; break ; + case 'd' : si.delim = localopt.arg ; break ; + default : return -3 ; + } + } + argc -= localopt.ind ; argv += localopt.ind ; + + if ((unsigned int)argc < 1+as) return -3 ; + if (!*argv[0] || el_vardupl(argv[0], info->vars.s, info->vars.len)) return -2 ; + if (!stralloc_catb(&info->vars, argv[0], str_len(argv[0]) + 1)) return -1 ; + x = env_get2(envp, argv[as]) ; + if (!x) + { + if (insist) strerr_dienotset(100, argv[as]) ; + x = defaultval ; + } + if (!x) blah.n = 0 ; + else + { + register int r ; + if (!stralloc_cats(&info->values, x)) goto err ; + r = el_transform(&info->values, blah.value, &si) ; + if (r < 0) goto err ; + blah.n = r ; + } + if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ; + return localopt.ind + 1 + as ; + + err: + info->vars.len = blah.var ; + info->values.len = blah.value ; + return -1 ; +} + +int exlsn_import (int argc, char const **argv, char const *const *envp, exlsn_t *info) +{ + return exlsn_import_as(argc, argv, envp, info, 0) ; +} + +int exlsn_importas (int argc, char const **argv, char const *const *envp, exlsn_t *info) +{ + return exlsn_import_as(argc, argv, envp, info, 1) ; +} diff --git a/src/libexecline/exlsn_main.c b/src/libexecline/exlsn_main.c new file mode 100644 index 0000000..e50fc2d --- /dev/null +++ b/src/libexecline/exlsn_main.c @@ -0,0 +1,19 @@ +/* ISC license. */ + +#include <skalibs/strerr2.h> +#include <execline/execline.h> +#include "exlsn.h" + +void exlsn_main (int argc, char const **argv, char const *const *envp, exlsnfunc_t *func, char const *usage) +{ + exlsn_t info = EXLSN_ZERO ; + int r = (*func)(argc, argv, envp, &info) ; + if (r < 0) switch (r) + { + case -3 : strerr_dieusage(100, usage) ; + case -2 : strerr_dief1x(111, "bad substitution key") ; + case -1 : strerr_diefu1sys(111, "complete exlsn function") ; + default : strerr_diefu2x(111, "complete exlsn function", ": unknown error") ; + } + el_substandrun(argc-r, argv+r, envp, &info) ; +} diff --git a/src/libexecline/exlsn_multidefine.c b/src/libexecline/exlsn_multidefine.c new file mode 100644 index 0000000..e64cbfd --- /dev/null +++ b/src/libexecline/exlsn_multidefine.c @@ -0,0 +1,79 @@ +/* ISC license. */ + +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <execline/execline.h> +#include "exlsn.h" + +int exlsn_multidefine (int argc, char const **argv, char const *const *envp, exlsn_t *info) +{ + eltransforminfo_t si = ELTRANSFORMINFO_ZERO ; + subgetopt_t localopt = SUBGETOPT_ZERO ; + unsigned int varbase = info->vars.len ; + unsigned int valbase = info->values.len ; + unsigned int pos = valbase ; + unsigned int i = 0 ; + unsigned int max ; + char const *x ; + int argc1 ; + int zeroword = 0, likeread = 0 ; + si.split = 1 ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "0rnCcd:", &localopt) ; + if (opt < 0) break ; + switch (opt) + { + case '0' : zeroword = 1 ; break ; + case 'r' : likeread = 1 ; break ; + case 'n' : si.chomp = 1 ; break ; + case 'C' : si.crunch = 1 ; break ; + case 'c' : si.crunch = 0 ; break ; + case 'd' : si.delim = localopt.arg ; break ; + default : return -3 ; + } + } + argc -= localopt.ind ; argv += localopt.ind ; + + if (argc < 2) return -3 ; + x = argv[0] ; + argv++ ; argc-- ; + argc1 = el_semicolon(argv) ; + if (argc1 >= argc) return -3 ; + if (!stralloc_cats(&info->values, x)) return -1 ; + { + register int r = el_transform(&info->values, valbase, &si) ; + if (r < 0) goto err ; + max = r ; + } + if (!stralloc_0(&info->values)) goto err ; + for (; i < (unsigned int)argc1 ; i++) + { + if (*argv[i]) + { + elsubst_t blah ; + blah.var = info->vars.len ; + if (el_vardupl(argv[i], info->vars.s, info->vars.len)) goto err2 ; + if (!stralloc_catb(&info->vars, argv[i], str_len(argv[i]) + 1)) goto err ; + blah.value = i < max ? pos : info->values.len - 1 ; + blah.n = (i < max) || !zeroword ; + if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ; + } + if (i < max) pos += str_len(info->values.s + pos) + 1 ; + } + if ((i < max) && likeread) genalloc_s(elsubst_t, &info->data)[i-1].n = max - i + 1 ; + + (void)envp ; + return localopt.ind + argc1 + 2 ; + + err: + info->vars.len = varbase ; + info->values.len = valbase ; + return -1 ; + err2: + info->vars.len = varbase ; + info->values.len = valbase ; + return -2 ; +} |