From 7733ade831f01a00b93e88312c433dbc3d2cdaa2 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 15 Mar 2009 01:17:05 +0000 Subject: Michael Hwang: 26731 with cosmetic changes: add "a" and "A" modifiers 26736: document the above --- ChangeLog | 9 +++- Doc/Zsh/expn.yo | 45 ++++++++++-------- Src/hist.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Src/subst.c | 14 ++++++ 4 files changed, 190 insertions(+), 19 deletions(-) diff --git a/ChangeLog b/ChangeLog index 34d3618d8..25d857495 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2009-03-15 Peter Stephenson + + * 26736: Doc/Zsh/expn.yo: document 26731. + + * Michael Hwang: 26731 (with cosmetic changes): Src/hist.c, + Src/subst.c: add `a' and `A' modifiers. + 2009-03-14 Wayne Davison * 26735: Src/Modules/files.c, Src/Modules/mapfile.c, @@ -11409,5 +11416,5 @@ ***************************************************** * This is used by the shell to define $ZSH_PATCHLEVEL -* $Revision: 1.4617 $ +* $Revision: 1.4618 $ ***************************************************** diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 446933e43..16d42fbfc 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -216,20 +216,24 @@ of em(filename generation) and em(parameter expansion), except where noted. startitem() -item(tt(h))( -Remove a trailing pathname component, leaving the head. This works -like `tt(dirname)'. +item(tt(a))( +Turn a file name into an absolute path: prepends the current directory, +if necessary, and resolves any use of `tt(..)' and `tt(.)' in the path. ) -item(tt(r))( -Remove a filename extension of the form `tt(.)var(xxx)', leaving -the root name. +item(tt(A))( +As `tt(a)', but also resolve use of symbolic links where possible. +Note that resolution of `tt(..)' occurs em(before) resolution of symbolic +links. ) item(tt(e))( Remove all but the extension. ) -item(tt(t))( -Remove all leading pathname components, leaving the tail. This works -like `tt(basename)'. +item(tt(h))( +Remove a trailing pathname component, leaving the head. This works +like `tt(dirname)'. +) +item(tt(l))( +Convert the words to all lowercase. ) item(tt(p))( Print the new command but do not execute it. Only works with history @@ -244,15 +248,9 @@ by tt(eval). item(tt(Q))( Remove one level of quotes from the substituted words. ) -item(tt(x))( -Like tt(q), but break into words at whitespace. Does not work with -parameter expansion. -) -item(tt(l))( -Convert the words to all lowercase. -) -item(tt(u))( -Convert the words to all uppercase. +item(tt(r))( +Remove a filename extension of the form `tt(.)var(xxx)', leaving +the root name. ) item(tt(s/)var(l)tt(/)var(r)[tt(/)])( Substitute var(r) for var(l) as described below. @@ -272,6 +270,17 @@ immediately by a tt(g). In parameter expansion the tt(&) must appear inside braces, and in filename generation it must be quoted with a backslash. ) +item(tt(t))( +Remove all leading pathname components, leaving the tail. This works +like `tt(basename)'. +) +item(tt(u))( +Convert the words to all uppercase. +) +item(tt(x))( +Like tt(q), but break into words at whitespace. Does not work with +parameter expansion. +) enditem() The tt(s/l/r/) substitution works as follows. By default the left-hand diff --git a/Src/hist.c b/Src/hist.c index 637976de7..c8625c7af 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(); @@ -1482,6 +1497,132 @@ hcomsearch(char *str) /* various utilities for : modifiers */ +/**/ +int +chabspath(char **junkptr) +{ + char *current, *dest; + + if (!**junkptr) + return 1; + + if (**junkptr != '/') { + *junkptr = zhtricat(zgetcwd(), "/", *junkptr); + } + + current = *junkptr; + 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) +{ + char *lastpos, *nonreal, real[PATH_MAX]; + + 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; + + lastpos = strend(*junkptr); + nonreal = lastpos + 1; + + 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) diff --git a/Src/subst.c b/Src/subst.c index 9e3f06fe3..5033dd492 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; -- cgit 1.4.1