diff options
Diffstat (limited to 'src/posix')
-rw-r--r-- | src/posix/deps-exe/posix-cd | 1 | ||||
-rw-r--r-- | src/posix/posix-cd.c | 155 |
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) ; +} |