summaryrefslogtreecommitdiff
path: root/src/posix
diff options
context:
space:
mode:
Diffstat (limited to 'src/posix')
-rw-r--r--src/posix/deps-exe/posix-cd1
-rw-r--r--src/posix/posix-cd.c155
2 files changed, 156 insertions, 0 deletions
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) ;
+}