Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
PATCH: zsh/files adjustments
- X-seq: zsh-workers 24972
- From: Phil Pennock <zsh-workers+phil.pennock@xxxxxxxxxxxx>
- To: Zsh hackers list <zsh-workers@xxxxxxxxxx>
- Subject: PATCH: zsh/files adjustments
- Date: Wed, 7 May 2008 23:08:13 -0700
- 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=;
- Mail-followup-to: Zsh hackers list <zsh-workers@xxxxxxxxxx>
- Mailing-list: contact zsh-workers-help@xxxxxxxxxx; run by ezmlm
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
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"
Messages sorted by:
Reverse Date,
Date,
Thread,
Author