diff --git a/ep.c b/ep.c index b07da67..c93382b 100644 --- a/ep.c +++ b/ep.c @@ -70,12 +70,18 @@ int main(int argc, char **argv) if (chroot) p("[chroot] "); + /* XXX: treat allocation failures as variables not existing in environment */ const char *home = getenv("HOME"); + char *pwd = getenv("PWD"); + #define cond_strdup(s) s = (s ? strdup(s) : s) + cond_strdup(home); + cond_strdup(pwd); + #undef cond_strdup /* show we are on a different machine */ print_ssh(); - print_pwd(home); + print_pwd(home, pwd); /* git status */ void *git_info; diff --git a/ep.h b/ep.h index b28e0aa..6c1cd3f 100644 --- a/ep.h +++ b/ep.h @@ -24,7 +24,7 @@ void e(enum log_level_value, const char *, int); /* from path.c */ extern const int fish_style_dir; -void print_pwd(const char *); +void print_pwd(const char *, char *); /* from git.c */ void *git_thread(void *); diff --git a/path.c b/path.c index 2374969..79739e6 100644 --- a/path.c +++ b/path.c @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include "ep.h" #include "info_strings.h" @@ -8,41 +10,82 @@ /* print current dir in fish style */ const int fish_style_dir = 1; -void print_pwd(const char *home) +static inline int file_match(const struct stat *s1, const struct stat *s2) { - char *pwd = get_current_dir_name(), *rpwd = pwd; + return s1->st_dev == s2->st_dev && s1->st_ino == s2->st_ino; +} + +void print_pwd(const char *home, char *envpwd) +{ + struct stat st_pwd, st_envpwd, st_home; + char pwd[PATH_MAX]; + char *mrpwd = getcwd(pwd, sizeof pwd); + /* const alias to avoid accidentally modifying m(utable)rpwd. + * remember to ALWAYS change both variables */ + const char *rpwd = mrpwd; if (!rpwd) { - /* get_current_dir_name failed */ + /* getcwd failed */ p(unknowndir); } else { + /* if envpwd exists and is valid (starts with '/' and matches current dir), + * replace pwd with it */ + if (envpwd && envpwd[0] == '/') { + /* if we can't stat current dir, bail */ + if (stat(".", &st_pwd)) { + p(unknowndir); + return; + } + if (!stat(envpwd, &st_envpwd) && file_match(&st_pwd, &st_envpwd)) { + rpwd = mrpwd = envpwd; + } + } else { + /* invalidate envpwd so it can be used to determine if st_pwd was initialized */ + envpwd = NULL; + } /* strip HOME out if possible */ if (home) { size_t l = strlen(home); - if (!strncmp(home, rpwd, l)) { + if (!strncmp(home, rpwd, l) && (rpwd[l] == 0 || rpwd[l] == '/')) { /* found HOME in pwd */ p("~"); - rpwd += l; - if (!*rpwd) - /* printing ~ is enough - * if it goes on, it will print ~/ */ - goto end; + /* advance both pwd pointers */ + rpwd = (mrpwd += l); + /* check if we are in HOME. + * if yes, printing '~' is all we want */ + if ( + /* pwd is only HOME */ + rpwd[0] == 0 || + /* current dir is HOME anyway */ + !stat(home, &st_home) && + (envpwd || !stat(".", &st_pwd)) && /* only stat(".") if it hasn't happened before */ + file_match(&st_home, &st_pwd)) + return; } - if (fish_style_dir) { - /* short and sweet way of malloc-ing enough memory */ - char *frpwd = strdup(rpwd); + /* starting from here, rpwd always starts with '/'. + * it can be a path relative to HOME or an absolute path */ - /* rpwd starts with a slash */ - const char *c = rpwd, *co; - char *n = frpwd; - for (; c; co = c, c = strchr(co+1, '/')) { - *n++ = '/'; - *n++ = *(c+1); + if (fish_style_dir) { + char *saveptr; + const char *tok, *oldtok = NULL; + while ((tok = strtok_r(mrpwd, "/", &saveptr))) { + mrpwd = NULL; + if (!strcmp(tok, "..")) { + p("/.."); + } else { + char str[] = {'/', tok[0], 0}; + p(str); + } + + oldtok = tok; + } + /* print the last token in full */ + if (oldtok) + p(oldtok+1); + /* if no token was found, we are in root */ + if (mrpwd) { + p("/"); } - /* copy last path completely */ - strcpy(--n, co+1); - p(frpwd); - free(frpwd); } else { p(rpwd); } @@ -51,7 +94,4 @@ void print_pwd(const char *home) p(rpwd); } } - -end: - free(pwd); }