summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2019-10-09 17:24:15 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2019-10-09 17:24:15 +0000
commit31694907dde18fc70d7b3e3813e5183d35f0e5db (patch)
tree8a00ba5d8e40180a0c0ac81c26d97506aa8fdde4
parent719337e34b0e3030acab2786c3035bbd3fcceb93 (diff)
downloadexecline-31694907dde18fc70d7b3e3813e5183d35f0e5db.tar.xz
Add --enable-pedantic-posix, posix-cd, prepare for 2.5.3.0
Also make wait posix-compliant and update doc.
-rw-r--r--.gitignore1
-rw-r--r--INSTALL2
-rw-r--r--NEWS10
-rwxr-xr-xconfigure11
-rw-r--r--doc/cd.html12
-rw-r--r--doc/index.html5
-rw-r--r--doc/posix-cd.html62
-rw-r--r--doc/upgrade.html10
-rw-r--r--doc/wait.html10
-rw-r--r--package/deps.mak3
-rw-r--r--package/info2
-rw-r--r--package/modes1
-rw-r--r--package/targets.mak9
-rw-r--r--src/execline/wait.c54
-rw-r--r--src/posix/deps-exe/posix-cd1
-rw-r--r--src/posix/posix-cd.c155
16 files changed, 324 insertions, 24 deletions
diff --git a/.gitignore b/.gitignore
index 4c9e807..0631586 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,7 @@
/multisubstitute
/pipeline
/piperw
+/posix-cd
/redirfd
/runblock
/shift
diff --git a/INSTALL b/INSTALL
index 0534bbe..4dc76f4 100644
--- a/INSTALL
+++ b/INSTALL
@@ -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:
diff --git a/NEWS b/NEWS
index 35ce3d4..e0492aa 100644
--- a/NEWS
+++ b/NEWS
@@ -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
----------
diff --git a/configure b/configure
index 0013e53..ce3939d 100755
--- a/configure
+++ b/configure
@@ -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) ;
+}