From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 5727 invoked from network); 8 May 2008 06:08:33 -0000 X-Spam-Checker-Version: SpamAssassin 3.2.4 (2008-01-01) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=AWL,BAYES_00 autolearn=ham version=3.2.4 Received: from news.dotsrc.org (HELO a.mx.sunsite.dk) (130.225.247.88) by ns1.primenet.com.au with SMTP; 8 May 2008 06:08:33 -0000 Received-SPF: none (ns1.primenet.com.au: domain at sunsite.dk does not designate permitted sender hosts) Received: (qmail 1288 invoked from network); 8 May 2008 06:08:28 -0000 Received: from sunsite.dk (130.225.247.90) by a.mx.sunsite.dk with SMTP; 8 May 2008 06:08:28 -0000 Received: (qmail 10727 invoked by alias); 8 May 2008 06:08:25 -0000 Mailing-List: contact zsh-workers-help@sunsite.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 24972 Received: (qmail 10710 invoked from network); 8 May 2008 06:08:24 -0000 Received: from bifrost.dotsrc.org (130.225.254.106) by sunsite.dk with SMTP; 8 May 2008 06:08:24 -0000 Received: from mx.spodhuis.org (redoubt.spodhuis.org [193.202.115.177]) by bifrost.dotsrc.org (Postfix) with ESMTP id 3042E80ED172 for ; Thu, 8 May 2008 08:08:17 +0200 (CEST) DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=d200803; d=spodhuis.org; h=Received:Date:From:To:Subject:Message-ID:Mail-Followup-To:MIME-Version:Content-Type:Content-Disposition; b=GNajIGwZSoVzpCv/pHXAqWR1ZVfEEadAaeGt7+zH9Kbnakhnu5oz233wdjtREUBs7oS0xzlv/xWGSwUbFA5hwi9YC2CPhabVwf7lDSfIz6HYGL9qDEnks0yu9uq4J4EVJbVLkgPIu09E4yzvT/y/Fq6wRLVkqIPgZ0lT30lNHm8=; Received: by smtp.spodhuis.org with local id 1JtzIb-000Dtt-9D; Thu, 08 May 2008 06:08:13 +0000 Date: Wed, 7 May 2008 23:08:13 -0700 From: Phil Pennock To: Zsh hackers list Subject: PATCH: zsh/files adjustments Message-ID: <20080508060813.GA53310@redoubt.spodhuis.org> Mail-Followup-To: Zsh hackers list MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="vtzGhvizbBRQ85DL" Content-Disposition: inline X-Virus-Scanned: ClamAV 0.91.2/7059/Thu May 8 07:03:47 2008 on bifrost X-Virus-Status: Clean --vtzGhvizbBRQ85DL Content-Type: text/plain; charset=us-ascii Content-Disposition: inline This patch for zsh/files: * Updates the documentation to be more cautionary * Adds new binary names for the commands * Implements ln -h/-n to inhibit chasing symlinks On this last point: the BSD systems all have a fairly consistent set of options for dealing with how symlinks are chased, documented in symlink(7), which is why they have "ln -h" where GNU went with "ln -n"; the BSDs nowadays implement -n as a compatibility option. I decided to add both. To rip an example from one I contributed to the OpenBSD man-page for ln(1): ----------------------------8< cut here >8------------------------------ In the next example, the second call to ln removes the original foo and creates a replacement pointing to baz: $ mkdir bar baz $ ln -s bar foo $ ln -shf baz foo Without the -h option, this would instead leave foo pointing to bar and inside foo create a new symlink baz pointing to itself. This results from directory-walking. ----------------------------8< cut here >8------------------------------ Is there any thought to being able to name groups of features, perhaps with a g: prefix, so that it becomes possible to do: zmodload -F zsh/files +g:zf_commands zmodload -F zsh/files -g:bare_commands ? Is this desirable? I'd view the semantics as batch-enable/disable, so that later items on the command-line override, even if more general. Feedback appreciated, -Phil --vtzGhvizbBRQ85DL Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="files-ln.patch" Index: Doc/Zsh/mod_files.yo =================================================================== RCS file: /home/cvsroot/zsh/Doc/Zsh/mod_files.yo,v retrieving revision 1.3 diff -p -u -r1.3 mod_files.yo --- Doc/Zsh/mod_files.yo 21 Aug 2007 22:59:49 -0000 1.3 +++ Doc/Zsh/mod_files.yo 8 May 2008 05:54:02 -0000 @@ -2,7 +2,17 @@ COMMENT(!MOD!zsh/files Some basic file manipulation commands as builtins. !MOD!) cindex(files, manipulating) -The tt(zsh/files) module makes some standard commands available as builtins: +The tt(zsh/files) module makes available some common commands for file +manipulation as builtins; these commands are probably not needed for +many normal situations but can be useful in emergency recovery +situations with constrained resources. The commands do not implement +all features now required by relevant standards committees. + +For all commands, a variant beginning tt(zf_) is also available and loaded +automatically. Using the features capability of zmodload will let you load +only those names you want. + +The commands loaded by default are: startitem() findex(chgrp) @@ -51,8 +61,8 @@ a deep directory tree can't end up recur a result of directories being moved up the tree. ) findex(ln) -xitem(tt(ln) [ tt(-dfis) ] var(filename) var(dest)) -item(tt(ln) [ tt(-dfis) ] var(filename) ... var(dir))( +xitem(tt(ln) [ tt(-dfhins) ] var(filename) var(dest)) +item(tt(ln) [ tt(-dfhins) ] var(filename) ... var(dir))( Creates hard (or, with tt(-s), symbolic) links. In the first form, the specified var(dest)ination is created, as a link to the specified var(filename). In the second form, each of the var(filename)s is @@ -69,6 +79,16 @@ By default, existing files cannot be rep The tt(-i) option causes the user to be queried about replacing existing files. The tt(-f) option causes existing files to be silently deleted, without querying. tt(-f) takes precedence. + +The tt(-h) and tt(-n) options are identical and both exist for +compatibility; either one indicates that if the target is a symlink +then it should not be dereferenced. +Typically this is used in combination with tt(-sf) so that if an +existing link points to a directory then it will be removed, +instead of followed. +If this option is used with multiple filenames and the target +is a symbolic link pointing to a directory then the result is +an error. ) findex(mkdir) item(tt(mkdir) [ tt(-p) ] [ tt(-m) var(mode) ] var(dir) ...)( Index: Src/Modules/files.c =================================================================== RCS file: /home/cvsroot/zsh/Src/Modules/files.c,v retrieving revision 1.18 diff -p -u -r1.18 files.c --- Src/Modules/files.c 21 Aug 2007 22:59:49 -0000 1.18 +++ Src/Modules/files.c 8 May 2008 05:56:18 -0000 @@ -166,23 +166,37 @@ bin_rmdir(char *nam, char **args, UNUSED #define BIN_LN 0 #define BIN_MV 1 -#define MV_NODIRS (1<<0) -#define MV_FORCE (1<<1) -#define MV_INTER (1<<2) -#define MV_ASKNW (1<<3) -#define MV_ATOMIC (1<<4) - -/* bin_ln actually does three related jobs: hard linking, symbolic * - * linking, and renaming. If called as mv it renames, otherwise * - * it looks at the -s option. If hard linking, it will refuse to * - * attempt linking to a directory unless the -d option is given. */ +#define MV_NODIRS (1<<0) +#define MV_FORCE (1<<1) +#define MV_INTERACTIVE (1<<2) +#define MV_ASKNW (1<<3) +#define MV_ATOMIC (1<<4) +#define MV_NOCHASETARGET (1<<5) + +/* + * bin_ln actually does three related jobs: hard linking, symbolic + * linking, and renaming. If called as mv it renames, otherwise + * it looks at the -s option. If hard linking, it will refuse to + * attempt linking to a directory unless the -d option is given. + */ + +/* + * Option compatibility: BSD systems settled on using mostly-standardised + * options across multiple commands to deal with symlinks; see, eg, + * symlink(7) on a *BSD system for details. Per this, to work on a link + * directly we use "-h" and "ln -hsf" will not follow the target if it + * points to a directory. GNU settled on using -n for ln(1), so we + * have "ln -nsf". We handle them both. + * + * Logic compared against that of FreeBSD's ln.c, compatible license. + */ /**/ static int bin_ln(char *nam, char **args, Options ops, int func) { MoveFunc move; - int flags, err = 0; + int flags, have_dir, err = 0; char **a, *ptr, *rp, *buf; struct stat st; size_t blen; @@ -195,6 +209,8 @@ bin_ln(char *nam, char **args, Options o } else { flags = OPT_ISSET(ops,'f') ? MV_FORCE : 0; #ifdef HAVE_LSTAT + if(OPT_ISSET(ops,'h') || OPT_ISSET(ops,'n')) + flags |= MV_NOCHASETARGET; if(OPT_ISSET(ops,'s')) move = (MoveFunc) symlink; else @@ -206,12 +222,39 @@ bin_ln(char *nam, char **args, Options o } } if(OPT_ISSET(ops,'i') && !OPT_ISSET(ops,'f')) - flags |= MV_INTER; + flags |= MV_INTERACTIVE; for(a = args; a[1]; a++) ; if(a != args) { rp = unmeta(*a); - if(rp && !stat(rp, &st) && S_ISDIR(st.st_mode)) - goto havedir; + if(rp && !stat(rp, &st) && S_ISDIR(st.st_mode)) { + have_dir = 1; + if((flags & MV_NOCHASETARGET) + && !lstat(rp, &st) && S_ISLNK(st.st_mode)) { + /* + * So we have "ln -h" with the target being a symlink pointing + * to a directory; if there are multiple sources but the target + * is a symlink, then it's an error as we're not following + * symlinks; if OTOH there's just one source, then we need to + * either fail EEXIST or if "-f" given then remove the target. + */ + if(a > args+1) { + errno = ENOTDIR; + zwarnnam(nam, "%s: %e", *a, errno); + return 1; + } + if(flags & MV_FORCE) { + unlink(rp); + have_dir = 0; + } else { + errno = EEXIST; + zwarnnam(nam, "%s: %e", *a, errno); + return 1; + } + } + /* Normal case, target is a directory, chase into it */ + if (have_dir) + goto havedir; + } } if(a > args+1) { zwarnnam(nam, "last of many arguments must be a directory"); @@ -269,7 +312,7 @@ domove(char *nam, MoveFunc move, char *p zwarnnam(nam, "%s: cannot overwrite directory", q); zsfree(pbuf); return 1; - } else if(flags & MV_INTER) { + } else if(flags & MV_INTERACTIVE) { nicezputs(nam, stderr); fputs(": replace `", stderr); nicezputs(q, stderr); @@ -705,12 +748,14 @@ bin_chown(char *nam, char **args, Option /* module paraphernalia */ #ifdef HAVE_LSTAT -# define LN_OPTS "dfis" +# define LN_OPTS "dfhins" #else # define LN_OPTS "dfi" #endif static struct builtin bintab[] = { + /* The names which overlap commands without necessarily being + * fully compatible. */ BUILTIN("chgrp", 0, bin_chown, 2, -1, BIN_CHGRP, "hRs", NULL), BUILTIN("chown", 0, bin_chown, 2, -1, BIN_CHOWN, "hRs", NULL), BUILTIN("ln", 0, bin_ln, 1, -1, BIN_LN, LN_OPTS, NULL), @@ -719,6 +764,16 @@ static struct builtin bintab[] = { BUILTIN("rm", 0, bin_rm, 1, -1, 0, "dfirs", NULL), BUILTIN("rmdir", 0, bin_rmdir, 1, -1, 0, NULL, NULL), BUILTIN("sync", 0, bin_sync, 0, 0, 0, NULL, NULL), + /* The "safe" zsh-only names */ + BUILTIN("zf_chgrp", 0, bin_chown, 2, -1, BIN_CHGRP, "hRs", NULL), + BUILTIN("zf_chown", 0, bin_chown, 2, -1, BIN_CHOWN, "hRs", NULL), + BUILTIN("zf_ln", 0, bin_ln, 1, -1, BIN_LN, LN_OPTS, NULL), + BUILTIN("zf_mkdir", 0, bin_mkdir, 1, -1, 0, "pm:", NULL), + BUILTIN("zf_mv", 0, bin_ln, 2, -1, BIN_MV, "fi", NULL), + BUILTIN("zf_rm", 0, bin_rm, 1, -1, 0, "dfirs", NULL), + BUILTIN("zf_rmdir", 0, bin_rmdir, 1, -1, 0, NULL, NULL), + BUILTIN("zf_sync", 0, bin_sync, 0, 0, 0, NULL, NULL), + }; static struct features module_features = { Index: Src/Modules/files.mdd =================================================================== RCS file: /home/cvsroot/zsh/Src/Modules/files.mdd,v retrieving revision 1.3 diff -p -u -r1.3 files.mdd --- Src/Modules/files.mdd 20 Jun 2007 20:59:17 -0000 1.3 +++ Src/Modules/files.mdd 8 May 2008 04:18:56 -0000 @@ -2,6 +2,6 @@ name=zsh/files link=dynamic load=no -autofeatures="b:chgrp b:chown b:ln b:mkdir b:mv b:rm b:rmdir b:sync" +autofeatures="b:chgrp b:chown b:ln b:mkdir b:mv b:rm b:rmdir b:sync b:zf_chgrp b:zf_chown b:zf_ln b:zf_mkdir b:zf_mv b:zf_rm b:zf_rmdir b:zf_sync" objects="files.o" --vtzGhvizbBRQ85DL--