summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--Doc/Zsh/expn.yo45
-rw-r--r--Src/hist.c141
-rw-r--r--Src/subst.c14
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 <p.w.stephenson@ntlworld.com>
+
+ * 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 <wayned@users.sourceforge.net>
* 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();
@@ -1484,6 +1499,132 @@ hcomsearch(char *str)
/**/
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)
{
char *str = strend(*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(&copy);
+ break;
+ case 'A':
+ chrealpath(&copy);
+ break;
case 'h':
remtpath(&copy);
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;