Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
PATCH: Modifiers that implement realpath-like feature
- X-seq: zsh-workers 26722
- From: Michael Hwang <nomex45@xxxxxxxxx>
- To: zsh-workers@xxxxxxxxxx
- Subject: PATCH: Modifiers that implement realpath-like feature
- Date: Thu, 12 Mar 2009 15:42:25 -0700 (PDT)
- Dkim-signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s1024; t=1236897746; bh=mpRWoLIwF4QwilZLUNtebdT6zrNWmeY5vFSmQM1NoVs=; h=Message-ID:X-YMail-OSG:Received:X-Mailer:Date:From:Subject:To:MIME-Version:Content-Type; b=yfT8B5u0nQgdOOrq4CCM7QnBoO4IztIIOkiWsnBKpzStf79vUlKOcH4qepvkalGlv+mNqZVfVz2VEfpORIvlL6tX67Q9kwapC7XJKKdL7K99owY1RNtSP59CgJisI66W2+l6u16BoCepKFSZC22Sf6Vj+UoKEz1HHwntezuVBX8=
- Domainkey-signature: a=rsa-sha1; q=dns; c=nofws; s=s1024; d=yahoo.com; h=Message-ID:X-YMail-OSG:Received:X-Mailer:Date:From:Subject:To:MIME-Version:Content-Type; b=MMl2h73ZLevEVmxuImkXe4e8mhcf4ziRcm01Y53MvDQAvU8+Jzp/A3SVyA1j4gAynYhT3yg8CknXRVGl/m6nKbUY8V/KAX5dYc/e5OUTcVjEtWzXaSyiBjGBOwl+hdikFCS8Uzc0HJ/FYMUr9IG3A7prbtVu8yuzdjFdIXuPPqM=;
- Mailing-list: contact zsh-workers-help@xxxxxxxxxx; run by ezmlm
Hey zshers,
I'm particularly proud of this patch, as it's my first time contributing to an open source project. (I'm sure the novelty will wear off in a while.) It implements two history-style realpath-like modifiers for getting the absolute path of a file. I chose the letter 'a' for 'absolute' (r was already taken). Both 'a' and 'A' have different meanings. The uppercase A will resolve symbolic links.
There are a couple of caveats. Firstly, blank variables don't expand to anything. Secondly, the modifier does NOT care if the file exists. So:
% FILE='dne.txt' # dne.txt does not exist.
%print ${FILE:a}
/home/joe/dne.txt
That example used the lower case 'a', which only resolves './' and '../'. However, with the upper case 'A' modifier, any of the previous will do :a and symlink resolution. Again, it doesn't matter if the file doesn't exist.
% ln -s /media/cdrom0 a_link
% FILE='./a_link/dne.txt'
% print ${FILE:A}
/media/cdrom0/dne.txt
Third, trying to climb above root is allowed, but ignored. Consider:
% FILE='/../../../dne.txt'
% print ${FILE:a}
/dne.txt
The only time there will ever be '..'s in the expansion is if RFS_SUPERROOT is enabled. I've never heard of it, but since fixdir() in builtin.c (upon which my functions are based) has it, I added it in.
That's the new modifiers in a nutshell. The diff is pasted below. I'm curious about the difference between bicat() and dyncat(), tricat() and zhtricat(). I wasn't sure if one was more appropriate to use than the other, or if it didn't matter. In any case, enjoy!
Michael "Nomexous" Hwang
-----------------
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;
Messages sorted by:
Reverse Date,
Date,
Thread,
Author