From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 17010 invoked from network); 12 Mar 2009 22:44:51 -0000 X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00 autolearn=ham version=3.2.5 Received: from news.dotsrc.org (HELO a.mx.sunsite.dk) (130.225.247.88) by ns1.primenet.com.au with SMTP; 12 Mar 2009 22:44:51 -0000 Received-SPF: none (ns1.primenet.com.au: domain at sunsite.dk does not designate permitted sender hosts) Received: (qmail 92764 invoked from network); 12 Mar 2009 22:42:38 -0000 Received: from sunsite.dk (130.225.247.90) by a.mx.sunsite.dk with SMTP; 12 Mar 2009 22:42:38 -0000 Received: (qmail 16662 invoked by alias); 12 Mar 2009 22:42:32 -0000 Mailing-List: contact zsh-workers-help@sunsite.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 26722 Received: (qmail 16644 invoked from network); 12 Mar 2009 22:42:31 -0000 Received: from bifrost.dotsrc.org (130.225.254.106) by sunsite.dk with SMTP; 12 Mar 2009 22:42:31 -0000 Received: from web37308.mail.mud.yahoo.com (web37308.mail.mud.yahoo.com [209.191.90.251]) by bifrost.dotsrc.org (Postfix) with SMTP id 202E880307F8 for ; Thu, 12 Mar 2009 23:42:26 +0100 (CET) Received: (qmail 70422 invoked by uid 60001); 12 Mar 2009 22:42:26 -0000 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=; Message-ID: <18038.70384.qm@web37308.mail.mud.yahoo.com> X-YMail-OSG: iVhiCX8VM1m5XBUHd0T6nQDbsRTaxhFhhHk3dYASBGt84Rhq8.fnIC.32X8h.2.prfw2AA_au1UH1u3F7GIWeaNNqvR6783RKfGpH1.2SdlmnwgtvIBLOJ_X8OaPHToEnQnsXpKKt1nSHv8G0CS25.EobJHDCtEQmG5U2nqeX7gbQ6Lw_UKr0yxFbdwhtg-- Received: from [128.175.185.67] by web37308.mail.mud.yahoo.com via HTTP; Thu, 12 Mar 2009 15:42:25 PDT X-Mailer: YahooMailRC/1155.45 YahooMailWebService/0.7.289.1 Date: Thu, 12 Mar 2009 15:42:25 -0700 (PDT) From: Michael Hwang Subject: PATCH: Modifiers that implement realpath-like feature To: zsh-workers@sunsite.dk MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Virus-Scanned: ClamAV 0.92.1/9102/Thu Mar 12 21:54:00 2009 on bifrost X-Virus-Status: Clean 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;