diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | INSTALL | 2 | ||||
-rw-r--r-- | NEWS | 10 | ||||
-rwxr-xr-x | configure | 11 | ||||
-rw-r--r-- | doc/cd.html | 12 | ||||
-rw-r--r-- | doc/index.html | 5 | ||||
-rw-r--r-- | doc/posix-cd.html | 62 | ||||
-rw-r--r-- | doc/upgrade.html | 10 | ||||
-rw-r--r-- | doc/wait.html | 10 | ||||
-rw-r--r-- | package/deps.mak | 3 | ||||
-rw-r--r-- | package/info | 2 | ||||
-rw-r--r-- | package/modes | 1 | ||||
-rw-r--r-- | package/targets.mak | 9 | ||||
-rw-r--r-- | src/execline/wait.c | 54 | ||||
-rw-r--r-- | src/posix/deps-exe/posix-cd | 1 | ||||
-rw-r--r-- | src/posix/posix-cd.c | 155 |
16 files changed, 324 insertions, 24 deletions
@@ -42,6 +42,7 @@ /multisubstitute /pipeline /piperw +/posix-cd /redirfd /runblock /shift @@ -6,7 +6,7 @@ Build Instructions - A POSIX-compliant C development environment - GNU make version 3.81 or later - - skalibs version 2.9.0.0 or later: http://skarnet.org/software/skalibs/ + - skalibs version 2.9.1.0 or later: http://skarnet.org/software/skalibs/ This software will run on any operating system that implements POSIX.1-2008, available at: @@ -1,5 +1,15 @@ Changelog for execline. +In 2.5.3.0 +---------- + + - New configure option: --enable-pedantic-posix. This +makes the "cd" program a symbolic link to a "posix-cd" program +which is fully POSIX compliant. This makes distributions unable +to use the "execline breaks POSIX" pretext to refuse to package +execline correctly. + + In 2.5.2.0 ---------- @@ -47,6 +47,7 @@ Optional features: --enable-absolute-paths do not rely on PATH to access this package's binaries, hardcode absolute BINDIR/foobar paths instead [disabled] --enable-nsss use the nsss library for user information [disabled] + --enable-pedantic-posix use pedantically POSIX-compatible binaries [disabled] EOF exit 0 @@ -152,6 +153,7 @@ allpic=true slashpackage=false abspath=false usensss=false +pposix=false sproot= home= exthome= @@ -196,6 +198,9 @@ for arg ; do --disable-absolute-paths|--enable-absolute-paths=no) abspath=false ;; --enable-nsss|--enable-nsss=yes) usensss=true ;; --disable-nsss|--enable-nsss=no) usensss=false ;; + --enable-pedantic-posix|--enable-pedantic-posix=yes) pposix=true ;; + --disable-pedantic-posix|--enable-pedantic-posix=no) pposix=false ;; + --enable-*|--disable-*|--with-*|--without-*|--*dir=*) ;; --enable-*|--disable-*|--with-*|--without-*|--*dir=*) ;; --host=*|--target=*) target=${arg#*=} ;; --build=*) build=${arg#*=} ;; @@ -455,7 +460,11 @@ else echo "LIBNSSS :=" echo "MAYBEPTHREAD_LIB :=" fi - +if $pposix ; then + echo "PEDANTIC_POSIX := 1" +else + echo "PEDANTIC_POSIX :=" +fi exec 1>&3 3>&- echo " ... done." diff --git a/doc/cd.html b/doc/cd.html index c412e5e..fe64c19 100644 --- a/doc/cd.html +++ b/doc/cd.html @@ -37,10 +37,14 @@ system call on <em>dir</em>, then execs into <em>prog...</em>. <h2> Notes </h2> -<p> -<tt>cd</tt> is a standard shell builtin. Be careful if you want to -use the <tt>cd</tt> command outside of an <tt>execline</tt> script. -</p> +<ul> + <li> <tt>cd</tt> is a standard shell builtin. Be careful if you want to +use the <tt>cd</tt> command outside of an <tt>execline</tt> script </li> + <li> When execline has been configured with the <tt>--enable-pedantic-posix</tt> +option, the <tt>cd</tt> binary is actually a symbolic link to the +<a href="posix-cd.html">posix-cd</a> binary. </li> +</ul> + </body> </html> diff --git a/doc/index.html b/doc/index.html index 193f476..0a3643c 100644 --- a/doc/index.html +++ b/doc/index.html @@ -51,7 +51,7 @@ shell's syntax, and has no security issues. <li> A POSIX-compliant system with a standard C development environment </li> <li> GNU make, version 3.81 or later. </li> <li> <a href="//skarnet.org/software/skalibs/">skalibs</a> version -2.9.0.0 or later. It's a build-time requirement. It's also a run-time +2.9.1.0 or later. It's a build-time requirement. It's also a run-time requirement if you link against the shared version of the skalibs library. </li> </ul> @@ -66,7 +66,7 @@ library. </li> <h3> Download </h3> <ul> - <li> The current released version of execline is <a href="execline-2.5.2.0.tar.gz">2.5.2.0</a>. </li> + <li> The current released version of execline is <a href="execline-2.5.3.0.tar.gz">2.5.3.0</a>. </li> <li> Alternatively, you can checkout a copy of the <a href="//git.skarnet.org/cgi-bin/cgit.cgi/execline/">execline git repository</a>: @@ -126,6 +126,7 @@ to your installation: the shebang lines for your system might be something like </p> <ul> <li><a href="cd.html">The <tt>cd</tt> program</a></li> +<li><a href="posix-cd.html">The <tt>posix-cd</tt> program</a></li> <li><a href="umask.html">The <tt>umask</tt> program</a></li> <li><a href="emptyenv.html">The <tt>emptyenv</tt> program</a></li> <li><a href="envfile.html">The <tt>envfile</tt> program</a></li> diff --git a/doc/posix-cd.html b/doc/posix-cd.html new file mode 100644 index 0000000..3a24602 --- /dev/null +++ b/doc/posix-cd.html @@ -0,0 +1,62 @@ +<html> + <head> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>execline: the posix-cd command</title> + <meta name="Description" content="execline: the posix-cd command" /> + <meta name="Keywords" content="execline command cd chdir posix posix-cd" /> + <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> --> +</head> +<body> + +<p> +<a href="index.html">execline</a><br /> +<a href="//skarnet.org/software/">Software</a><br /> +<a href="//skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>posix-cd</tt> program </h1> + +<p> +<tt>posix-cd</tt> changes the current working directory to a +given directory, then executes a program. +</p> + +<h2> Interface </h2> + +<pre> + posix-cd <em>dir</em> <em>prog...</em> +</pre> + +<p> +<tt>posix-cd</tt> changes the current working directory to <em>dir</em> +according to the +<a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/cd.html">POSIX +specification for a <tt>cd</tt> external utility</a>. Then, if <em>prog...</em> is +not empty, it execs into it. +</p> + +<h2> Notes </h2> + +<ul> + <li> <tt>posix-cd</tt> is only available when execline has been configured +with the <tt>--enable-pedantic-posix</tt> option, and in this case, the +<a href="cd.html">cd</a> binary is a symbolic link to it. </li> + <li> <tt>posix-cd</tt> fully conforms to the +<a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/cd.html">POSIX +specification</a>. When <em>prog...</em> is not empty, the behaviour of a +<tt>cd</tt> utility is not specified by POSIX, so <tt>posix-cd</tt> extends +the spec to be actually useful and usable in an execline program with the same +interface as the regular execline <a href="cd.html">cd</a> command. </li> + <li> Nobody ever executes or needs the external version (i.e. not a shell +builtin) of the POSIX <tt>cd</tt> command. Compared to the regular execline +<a href="cd.html">cd</a>, <tt>posix-cd</tt> is uselessly bloated and slow. +The only reason it exists is that some distributions refuse to package +execline correctly unless it is strictly POSIX-compliant; the +<tt>--enable-pedantic-posix</tt> configure option is there to appease them. </li> +</ul> + + +</body> +</html> diff --git a/doc/upgrade.html b/doc/upgrade.html index 3191685..045803e 100644 --- a/doc/upgrade.html +++ b/doc/upgrade.html @@ -18,6 +18,16 @@ <h1> What has changed in execline </h1> +<h2> in 2.5.3.0 </h2> + +<ul> + <li> <a href="//skarnet.org/software/skalibs/"></a>skalibs</a> +dependency bumped to 2.9.1.0. </li> + <li> <tt>--enable-pedantic-posix</tt> configure option added. </li> + <li> <a href="posix-cd.html">posix-cd</a> binary added. </li> + <li> <a href="wait.html">wait</a> binary made POSIX-compliant. </li> +</ul> + <h2> in 2.5.2.0 </h2> <ul> diff --git a/doc/wait.html b/doc/wait.html index a57d28c..a092859 100644 --- a/doc/wait.html +++ b/doc/wait.html @@ -60,5 +60,15 @@ exec into <em>prog...</em>. This is the default. </li> will print an error message and exit 1. </li> </ul> +<h2> Notes </h2> + +<ul> + <li> For <a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/wait.html">POSIX +compatibility</a>, <tt>wait</tt> also works when it cannot find a block. +In that case, all its command line is interpreted as <em>pids...</em> +arguments and it does not execute into a program. Instead, it exits +with a conforming exit code. </li> +</ul> + </body> </html> diff --git a/package/deps.mak b/package/deps.mak index e058e9f..0d4b00f 100644 --- a/package/deps.mak +++ b/package/deps.mak @@ -71,6 +71,7 @@ src/libexecline/exlsn_free.o src/libexecline/exlsn_free.lo: src/libexecline/exls src/libexecline/exlsn_importas.o src/libexecline/exlsn_importas.lo: src/libexecline/exlsn_importas.c src/include/execline/execline.h src/include-local/exlsn.h src/libexecline/exlsn_main.o src/libexecline/exlsn_main.lo: src/libexecline/exlsn_main.c src/include/execline/execline.h src/include-local/exlsn.h src/libexecline/exlsn_multidefine.o src/libexecline/exlsn_multidefine.lo: src/libexecline/exlsn_multidefine.c src/include/execline/execline.h src/include-local/exlsn.h +src/posix/posix-cd.o src/posix/posix-cd.lo: src/posix/posix-cd.c background: EXTRA_LIBS := ${SPAWN_LIB} background: src/execline/background.o ${LIBEXECLINE} -lskarnet @@ -171,3 +172,5 @@ libexecline.a.xyzzy: src/libexecline/el_execsequence.lo src/libexecline/el_getst endif libexecline.so.xyzzy: EXTRA_LIBS := -lskarnet libexecline.so.xyzzy: src/libexecline/el_execsequence.lo src/libexecline/el_getstrict.lo src/libexecline/el_parse.lo src/libexecline/el_parse_from_buffer.lo src/libexecline/el_parse_from_string.lo src/libexecline/el_popenv.lo src/libexecline/el_pushenv.lo src/libexecline/el_semicolon.lo src/libexecline/el_spawn0.lo src/libexecline/el_spawn1.lo src/libexecline/el_substandrun.lo src/libexecline/el_substandrun_str.lo src/libexecline/el_substitute.lo src/libexecline/el_transform.lo src/libexecline/el_vardupl.lo src/libexecline/exlsn_define.lo src/libexecline/exlsn_elglob.lo src/libexecline/exlsn_importas.lo src/libexecline/exlsn_multidefine.lo src/libexecline/exlsn_exlp.lo src/libexecline/exlsn_main.lo src/libexecline/exlsn_free.lo src/libexecline/exlp.lo +posix-cd: EXTRA_LIBS := +posix-cd: src/posix/posix-cd.o -lskarnet diff --git a/package/info b/package/info index c721ad5..4a5dd27 100644 --- a/package/info +++ b/package/info @@ -1,4 +1,4 @@ package=execline -version=2.5.2.0 +version=2.5.3.0 category=admin package_macro_name=EXECLINE diff --git a/package/modes b/package/modes index 7852d06..dbe4972 100644 --- a/package/modes +++ b/package/modes @@ -44,3 +44,4 @@ umask 0755 unexport 0755 wait 0755 withstdinas 0755 +posix-cd 0755 diff --git a/package/targets.mak b/package/targets.mak index c076fa3..824fc26 100644 --- a/package/targets.mak +++ b/package/targets.mak @@ -49,3 +49,12 @@ withstdinas LIBEXEC_TARGETS := LIB_DEFS := EXECLINE=execline + +ifeq ($(PEDANTIC_POSIX),1) + +BIN_TARGETS += posix-cd + +$(DESTDIR)$(bindir)/cd: $(DESTDIR)$(bindir)/posix-cd + exec ./tools/install.sh -l posix-cd $(DESTDIR)$(bindir)/cd + +endif diff --git a/src/execline/wait.c b/src/execline/wait.c index 2fdbe80..2a2ead2 100644 --- a/src/execline/wait.c +++ b/src/execline/wait.c @@ -14,36 +14,50 @@ #define USAGE "wait [ -I | -i ] [ -r | -t timeout ] { pids... }" #define dieusage() strerr_dieusage(100, USAGE) -typedef int actfunc_t (pid_t *, unsigned int *) ; +typedef int actfunc_t (pid_t *, unsigned int *, int *) ; typedef actfunc_t *actfunc_t_ref ; -static inline void waitall (void) +static inline int waitall (void) { + int wstat = 0 ; pid_t r = 1 ; - while (r > 0) r = wait(0) ; - if (r < 0 && errno != ECHILD) strerr_diefu1sys(111, "wait") ; + while (r > 0) r = wait(&wstat) ; + if (r < 0) + { + if (errno != ECHILD) strerr_diefu1sys(111, "wait") ; + else return 127 ; + } + return wait_estatus(wstat) ; } -static int waitany (pid_t *dummytab, unsigned int *dummyn) +static int waitany (pid_t *dummytab, unsigned int *dummyn, int *res) { + int wstat ; pid_t r = 1 ; - while (r > 0) r = wait_nohang(0) ; - if (!r) return 1 ; + while (r > 0) r = wait_nohang(&wstat) ; + if (!r) return (*res = wait_estatus(wstat), 1) ; if (errno != ECHILD) strerr_diefu1sys(111, "wait") ; + *res = 127 ; (void)dummytab ; (void)dummyn ; return 0 ; } -static int waitintab (pid_t *tab, unsigned int *n) +static int waitintab (pid_t *tab, unsigned int *n, int *res) { unsigned int i = 0 ; for (; i < *n ; i++) { - pid_t r = waitpid(tab[i], 0, WNOHANG) ; + int wstat ; + pid_t r = waitpid(tab[i], &wstat, WNOHANG) ; if (r) { - if (r < 0 && errno != ECHILD) strerr_diefu1sys(111, "waitpid") ; + if (r < 0) + { + if (errno == ECHILD) *res = 127 ; + else strerr_diefu1sys(111, "waitpid") ; + } + else *res = wait_estatus(wstat) ; tab[i--] = tab[--(*n)] ; } } @@ -61,15 +75,16 @@ static inline void handle_signals (void) } } -static inline void mainloop (tain_t *deadline, int insist, actfunc_t_ref f, pid_t *tab, unsigned int *n) +static inline int mainloop (tain_t *deadline, int insist, actfunc_t_ref f, pid_t *tab, unsigned int *n) { iopause_fd x = { .events = IOPAUSE_READ } ; + int res = 0 ; x.fd = selfpipe_init() ; if (x.fd < 0) strerr_diefu1sys(111, "create selfpipe") ; if (selfpipe_trap(SIGCHLD) < 0) strerr_diefu1sys(111, "trap SIGCHLD") ; tain_now_set_stopwatch_g() ; tain_add_g(deadline, deadline) ; - while ((*f)(tab, n)) + while ((*f)(tab, n, &res)) { int r = iopause_g(&x, 1, deadline) ; if (r < 0) strerr_diefu1sys(111, "iopause") ; @@ -82,6 +97,7 @@ static inline void mainloop (tain_t *deadline, int insist, actfunc_t_ref f, pid_ else handle_signals() ; } selfpipe_finish() ; + return res ; } int main (int argc, char const **argv, char const *const *envp) @@ -90,6 +106,8 @@ int main (int argc, char const **argv, char const *const *envp) int argc1 ; int hastimeout = 0 ; int insist = 0 ; + int r ; + int hasblock ; PROG = "wait" ; { subgetopt_t l = SUBGETOPT_ZERO ; @@ -112,8 +130,13 @@ int main (int argc, char const **argv, char const *const *envp) else tto = tain_infinite_relative ; } argc1 = el_semicolon(argv) ; - if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; - if (!argc1 && !hastimeout) waitall() ; + if (argc1 >= argc) + { + hasblock = 0 ; + argc1 = argc ; + } + else hasblock = 1 ; + if (!argc1 && !hastimeout) r = waitall() ; else { actfunc_t_ref f = argc1 ? &waitintab : &waitany ; @@ -125,8 +148,9 @@ int main (int argc, char const **argv, char const *const *envp) for (; i < n ; i++) if (!pid0_scan(argv[i], tab+i)) strerr_dieusage(100, USAGE) ; } - mainloop(&tto, insist, f, tab, &n) ; + r = mainloop(&tto, insist, f, tab, &n) ; } + if (!hasblock) return r ; xpathexec0_run(argv + argc1 + 1, envp) ; } diff --git a/src/posix/deps-exe/posix-cd b/src/posix/deps-exe/posix-cd new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/posix/deps-exe/posix-cd @@ -0,0 +1 @@ +-lskarnet diff --git a/src/posix/posix-cd.c b/src/posix/posix-cd.c new file mode 100644 index 0000000..8a98981 --- /dev/null +++ b/src/posix/posix-cd.c @@ -0,0 +1,155 @@ +/* ISC license. */ + +#include <sys/stat.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <limits.h> +#include <locale.h> + +#include <skalibs/bytestr.h> +#include <skalibs/sgetopt.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/strerr2.h> +#include <skalibs/stralloc.h> +#include <skalibs/djbunix.h> + +#define USAGE "posix-cd [ -L | -P ] [ - | path ] [ prog... ]" +#define dieusage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_diefu1sys(100, "stralloc_catb") + +int main (int argc, char const **argv, char const *const *envp) +{ + int phy = 0 ; + int dopwd = 0 ; + char const *where ; + int got = 0 ; + stralloc sa = STRALLOC_ZERO ; + PROG = "posix-cd" ; + setlocale(LC_ALL, "") ; /* yeah, as if */ + + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "LP", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'L' : phy = 0 ; break ; + case 'P' : phy = 1 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) where = getenv("HOME") ; + else + { + where = *argv++ ; + if (!strcmp(where, "-")) + { + where = getenv("OLDPWD") ; + dopwd = 1 ; + } + } + if (!where || !where[0]) dieusage() ; + + if ((where[0] != '/' && (where[0] != '.')) || (where[1] && where[1] != '.')) + { + char const *cdpath = getenv("CDPATH") ; + if (cdpath) + { + size_t pos = 0 ; + size_t len = strlen(cdpath) ; + while (pos < len) + { + struct stat st ; + size_t m = byte_chr(cdpath + pos, len - pos, ':') ; + sa.len = 0 ; + if (m) + { + if (!stralloc_catb(&sa, cdpath + pos, m)) dienomem() ; + if (cdpath[pos + m - 1] != '/' && !stralloc_catb(&sa, "/", 1)) dienomem() ; + } + else if (!stralloc_catb(&sa, "./", 2)) dienomem() ; + if (!stralloc_cats(&sa, where) || !stralloc_0(&sa)) dienomem() ; + if (!stat(sa.s, &st) && S_ISDIR(st.st_mode)) + { + got = 1 ; + dopwd = 1 ; + break ; + } + pos += m+1 ; + } + } + } + + if (!got && (!stralloc_cats(&sa, where) || !stralloc_0(&sa))) dienomem() ; + + { + size_t sabase = sa.len ; + if (sagetcwd(&sa) < 0) strerr_diefu1sys(111, "getcwd") ; + if (!stralloc_0(&sa)) dienomem() ; + if (!pathexec_env("OLDPWD", sa.s + sabase)) dienomem() ; + sa.len = sabase ; + } + + if (!phy) + { + char const *x = getenv("PWD") ; + if (x && sa.s[0] != '/') + { + size_t len = strlen(x) ; + int doslash = len && x[len-1] != '/' ; + if (!stralloc_insertb(&sa, 0, x, len + doslash)) dienomem() ; + if (doslash) sa.s[len] = '/' ; + } + { + stralloc tmp = STRALLOC_ZERO ; + if (!stralloc_ready(&tmp, sa.len + 2)) dienomem() ; + if (!path_canonicalize(tmp.s, sa.s, 1)) + strerr_diefu4sys(111, "canonicalize ", sa.s, ": problem with ", tmp.s) ; + stralloc_free(&sa) ; + sa = tmp ; + } + if (!pathexec_env("PWD", sa.s)) dienomem() ; + if (sa.len > PATH_MAX && strlen(where) < PATH_MAX && x && *x) + { + size_t len = strlen(x) ; + int hasslash = x[len-1] == '/' ; + if (!strncmp(sa.s, x, len)) + { + if (hasslash || (sa.len > len && sa.s[len] == '/')) + { + sa.len -= len + !hasslash ; + memmove(sa.s, sa.s + len + !hasslash, sa.len) ; + } + } + } + } + + /* fking finally */ + + if (chdir(sa.s) < 0) + strerr_diefu2sys(111, "chdir to ", where) ; + + /* and there's still more nonsense to do afterwards! */ + + if (phy) + { + sa.len = 0 ; + if (sagetcwd(&sa) < 0) strerr_diefu1sys(111, "getcwd") ; + if (!stralloc_0(&sa)) dienomem() ; + if (!pathexec_env("PWD", sa.s)) dienomem() ; + } + + if (dopwd) + { + sa.s[sa.len - 1] = '\n' ; + if (allwrite(1, sa.s, sa.len) < sa.len) + strerr_diefu1sys(111, "write to stdout") ; + } + + xpathexec0(argv) ; +} |