diff --git a/Src/hist.c b/Src/hist.c index 38ceac3..0dd8c21 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -623,6 +623,21 @@ histsubchar(int c) case 'p': histdone = HISTFLAG_DONE | HISTFLAG_NOEXEC; break; + case 'a': + if (!chabspath(&sline)) { + herrflush(); + zerr("modifier failed: a"); + return -1; + } + break; + + case 'A': + if (!chrealpath(&sline)) { + herrflush(); + zerr("modifier failed: A"); + return -1; + } + break; case 'h': if (!remtpath(&sline)) { herrflush(); @@ -1484,6 +1499,119 @@ hcomsearch(char *str) /**/ int +chabspath(char **junkptr) +{ + if (!**junkptr) + return 1; + + if (**junkptr != '/') { + *junkptr = zhtricat(zgetcwd(), "/", *junkptr); + } + + char *current = *junkptr; + char *dest = *junkptr; + +#ifdef HAVE_SUPERROOT + while (*current == '/' && current[1] == '.' && current[2] == '.' && (!current[3] || current[3] == '/')) { + *dest++ = '/'; + *dest++ = '.'; + *dest++ = '.'; + current += 3; + } +#endif + + for (;;) { + if (*current == '/') { +#ifdef __CYGWIN__ + if (current == *junkptr && current[1] == '/') + *dest++ = *current++; +#endif + *dest++ = *current++; + while (*current == '/') + current++; + } else if (!*current) { + while (dest > *junkptr + 1 && dest[-1] == '/') + dest--; + *dest = '\0'; + break; + } else if (current[0] == '.' && current[1] == '.' && (!current[2] || current[2] == '/')) { + if (current == *junkptr || dest == *junkptr) { + *dest++ = '.'; + *dest++ = '.'; + current += 2; + } else if (dest > *junkptr + 2 && !strncmp(dest - 3, "../", 3)) { + *dest++ = '.'; + *dest++ = '.'; + current += 2; + } else if (dest > *junkptr + 1) { + *dest = '\0'; + for (dest--; dest > *junkptr + 1 && dest[-1] != '/'; dest--); + if (dest[-1] != '/') + dest--; + current += 2; + } else if (dest == *junkptr + 1) { /* This might break with Cygwin's leading double slashes? */ + current += 2; + } else { + return 0; + } + } else if (current[0] == '.' && (current[1] == '/' || !current[1])) { + while (*++current == '/'); + } else { + while (*current != '/' && *current != '\0') + if ((*dest++ = *current++) == Meta) + dest[-1] = *current++ ^ 32; + } + } + return 1; +} + +/**/ +int +chrealpath(char **junkptr) +{ + if (!**junkptr) + return 1; + + /* Notice that this means ..'s are applied before symlinks are resolved! */ + if (!chabspath(junkptr)) + return 0; + + /* Notice that this means you cannot pass relative paths into this function! */ + if (**junkptr != '/') + return 0; + + char *lastpos = strend(*junkptr); + char *nonreal = lastpos + 1; + char real[PATH_MAX]; + + while (!realpath(*junkptr, real)) { + if (errno == EINVAL || errno == ELOOP || errno == ENAMETOOLONG || errno == ENOMEM) + return 0; + + if (nonreal == *junkptr) { + *real = '\0'; + break; + } + + while (*nonreal != '/' && nonreal >= *junkptr) + nonreal--; + *nonreal = '\0'; + } + + char *str = nonreal; + while (str <= lastpos) { + if (*str == '\0') + *str = '/'; + str++; + } + + *junkptr = bicat(real, nonreal); + + return 1; +} + +/**/ +int remtpath(char **junkptr) { char *str = strend(*junkptr); diff --git a/Src/subst.c b/Src/subst.c index 9e3f06f..5033dd4 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -3199,6 +3199,8 @@ modify(char **str, char **ptr) for (; !c && **ptr;) { switch (**ptr) { + case 'a': + case 'A': case 'h': case 'r': case 'e': @@ -3337,6 +3339,12 @@ modify(char **str, char **ptr) copy = dupstring(tt); *e = tc; switch (c) { + case 'a': + chabspath(©); + break; + case 'A': + chrealpath(©); + break; case 'h': remtpath(©); break; @@ -3396,6 +3404,12 @@ modify(char **str, char **ptr) } else { switch (c) { + case 'a': + chabspath(str); + break; + case 'A': + chrealpath(str); + break; case 'h': remtpath(str); break;