about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--Doc/Zsh/options.yo9
-rw-r--r--Etc/FAQ.yo3
-rw-r--r--NEWS16
-rw-r--r--Src/hist.c109
-rw-r--r--Src/options.c1
-rw-r--r--Src/zsh.h1
7 files changed, 141 insertions, 6 deletions
diff --git a/ChangeLog b/ChangeLog
index fa900b331..45acd817d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2008-04-17  Peter Stephenson  <pws@csr.com>
+
+	* unposted: NEWS: list new features so far since 4.3.6.
+
+	* Vincent Lefevre: 24818: Doc/Zsh/options.yo (modified),
+	Src/hist.c (with #ifdef HAVE_TRUNCATE), Src/options.c, Src/zsh.h:
+	history file locking using fcntl().
+
 2008-04-16  Clint Adams  <clint@zsh.org>
 
 	* 248??: Completion/Unix/Command/_git: some fixes and updates for
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 5b41de725..c0a3f65b8 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -577,6 +577,15 @@ than tt(SAVEHIST) in order to give you some room for the duplicated
 events, otherwise this option will behave just like
 tt(HIST_IGNORE_ALL_DUPS) once the history fills up with unique events.
 )
+pindex(HIST_FCNTL_LOCK)
+item(tt(HIST_FCNTL_LOCK))(
+When writing out the history file, by default zsh uses ad-hoc file locking
+to avoid known problems with locking on some operating systems.  With this
+option locking is done by means of the system's tt(fcntl) call, where
+this method is available.  On recent operating systems this may
+provide better performance, in particular avoiding history corruption when
+files are stored on NFS.
+)
 pindex(HIST_FIND_NO_DUPS)
 cindex(history, ignoring duplicates in search)
 item(tt(HIST_FIND_NO_DUPS))(
diff --git a/Etc/FAQ.yo b/Etc/FAQ.yo
index 238afcd3c..62f1842f3 100644
--- a/Etc/FAQ.yo
+++ b/Etc/FAQ.yo
@@ -2046,7 +2046,8 @@ sect(How does zsh handle multibyte input and output?)
   all terminals handle this, even if they correctly display the base
   multibyte character, this option is not on by default.  The KDE terminal
   emulator tt(konsole), tt(rxvt-unicode), and the Unicode version of
-  xterm, tt(uxterm), are known to handle combining characters.
+  xterm, tt(xterm -u8) or the front-end tt(uxterm), are known to handle
+  combining characters.
 
   The tt(COMBINING_CHARS) option only affects output; combining characters
   may always be input, but when the option is off will be displayed
diff --git a/NEWS b/NEWS
index 5ab7bd42d..17cb9ddb8 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,22 @@ CHANGES FROM PREVIOUS VERSIONS OF ZSH
 
 Note also the list of incompatibilities in the README file.
 
+Major changes between versions 4.3.6 and 4.3.7
+----------------------------------------------
+
+The option COMBINING_CHARS has been added.  When it is set, the
+line editor assumes the terminal is capable of displaying zero-width
+combining characters (typically accents) correctly as modifications
+to the base character, and will act accordingly.
+
+The option HIST_FCNTL_LOCK has been added to provide locking of history
+files using the system call fcntl().  On recent NFS implementations this
+may provide better reliability.
+
+Highlighting of sections of the command line is now supported, controlled
+by the array parameter zle_highlight and the ZLE special parameter
+REGION_HIGHLIGHT.
+
 Major changes between versions 4.3.5 and 4.3.6
 ----------------------------------------------
 
diff --git a/Src/hist.c b/Src/hist.c
index 58a8c61ff..6deb009d5 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -2021,6 +2021,20 @@ readhistfile(char *fn, int err, int readflags)
     else if (!lockhistfile(fn, 1))
 	return;
     if ((in = fopen(unmeta(fn), "r"))) {
+#ifdef HAVE_FCNTL_H
+	if (isset(HISTFCNTLLOCK)) {
+	    struct flock lck;
+
+	    lck.l_type = F_RDLCK;
+	    lck.l_whence = SEEK_SET;
+	    lck.l_start = 0;
+	    lck.l_len = 0;  /* lock the whole file */
+	    if (fcntl(fileno(in), F_SETLKW, &lck) == -1) {
+		fclose(in);
+		return;
+	    }
+	}
+#endif
 	nwordlist = 64;
 	wordlist = (short *)zalloc(nwordlist*sizeof(short));
 	bufsiz = 1024;
@@ -2149,6 +2163,68 @@ readhistfile(char *fn, int err, int readflags)
     unlockhistfile(fn);
 }
 
+#ifdef HAVE_FCNTL_H
+/**/
+static int
+wlockfile(int fd)
+{
+    struct flock lck;
+    int ctr = 8;
+
+    lck.l_type = F_WRLCK;
+    lck.l_whence = SEEK_SET;
+    lck.l_start = 0;
+    lck.l_len = 0;
+    while (fcntl(fd, F_SETLKW, &lck) == -1) {
+	if (--ctr < 0)
+	    return 1;
+	sleep (1);
+    }
+    return 0;
+}
+#endif
+
+/**/
+static int
+safe_unlink(const char *pathname)
+{
+#ifdef HAVE_FCNTL_H
+    if (isset(HISTFCNTLLOCK)) {
+	int fd = open(pathname, O_WRONLY | O_NOCTTY, 0600);
+	if (fd >= 0) {
+	    int err = wlockfile(fd) || unlink(pathname);
+	    close(fd);
+	    return err;
+	} else {
+	    return errno != ENOENT;
+	}
+    }
+#endif
+    return unlink(pathname) && errno != ENOENT;
+}
+
+/**/
+static int
+safe_rename(const char *oldpath, const char *newpath)
+{
+#ifdef HAVE_FCNTL_H
+    if (isset(HISTFCNTLLOCK)) {
+	int fd = open(newpath, O_CREAT | O_WRONLY | O_NOCTTY, 0600);
+	if (fd < 0) {
+	    return 1;
+	} else if (wlockfile(fd)) {
+	    close(fd);
+	    return 1;
+	} else {
+	    int err = rename(oldpath, newpath);
+	    close(fd);
+	    return err;
+	}
+    }
+#endif
+    return rename(oldpath, newpath);
+}
+
 /**/
 void
 savehistfile(char *fn, int err, int writeflags)
@@ -2158,6 +2234,9 @@ savehistfile(char *fn, int err, int writeflags)
     Histent he;
     zlong xcurhist = curhist - !!(histactive & HA_ACTIVE);
     int extended_history = isset(EXTENDEDHISTORY);
+#ifdef HAVE_FTRUNCATE
+    int truncate_history = 0;
+#endif
     int ret;
 
     if (!interact || savehistsiz <= 0 || !hist_ring
@@ -2196,12 +2275,16 @@ savehistfile(char *fn, int err, int writeflags)
 	tmpfile = NULL;
 	out = fd >= 0 ? fdopen(fd, "a") : NULL;
     } else if (!isset(HISTSAVEBYCOPY)) {
-	int fd = open(unmeta(fn), O_CREAT | O_WRONLY | O_TRUNC | O_NOCTTY, 0600);
+	int fd = open(unmeta(fn), O_CREAT | O_WRONLY | O_NOCTTY, 0600);
 	tmpfile = NULL;
 	out = fd >= 0 ? fdopen(fd, "w") : NULL;
+#ifdef HAVE_FTRUNCATE
+	/* The file should be truncated after its locking. */
+	truncate_history = 1;
+#endif
     } else {
 	tmpfile = bicat(unmeta(fn), ".new");
-	if (unlink(tmpfile) < 0 && errno != ENOENT)
+	if (safe_unlink(tmpfile))
 	    out = NULL;
 	else {
 	    struct stat sb;
@@ -2239,7 +2322,18 @@ savehistfile(char *fn, int err, int writeflags)
 #endif
 	}
     }
+#ifdef HAVE_FCNTL_H
+    if (out && isset(HISTFCNTLLOCK) && wlockfile(fileno(out))) {
+	zerr("can't lock file (timeout) -- history %s not updated", fn);
+	err = 0; /* Don't report a generic error below. */
+	out = NULL;
+    }
+#endif
     if (out) {
+#ifdef HAVE_FTRUNCATE
+	if (truncate_history)
+	    ftruncate(fileno(out), 0);
+#endif
 	ret = 0;
 	for (; he && he->histnum <= xcurhist; he = down_histent(he)) {
 	    if ((writeflags & HFILE_SKIPDUPS && he->node.flags & HIST_DUP)
@@ -2285,16 +2379,19 @@ savehistfile(char *fn, int err, int writeflags)
 		zsfree(lasthist.text);
 		lasthist.text = ztrdup(start);
 	    }
-	}
-	if (fclose(out) < 0 && ret >= 0)
+	} else if (ret >= 0 && fflush(out) < 0) {
 	    ret = -1;
+	}
 	if (ret >= 0) {
 	    if (tmpfile) {
-		if (rename(tmpfile, unmeta(fn)) < 0)
+		/* out has been flushed and the file must be renamed while
+		   being open so that the lock is still valid */
+		if (safe_rename(tmpfile, unmeta(fn)))
 		    zerr("can't rename %s.new to $HISTFILE", fn);
 		free(tmpfile);
 		tmpfile = NULL;
 	    }
+	    fclose(out);
 
 	    if (writeflags & HFILE_SKIPOLD
 		&& !(writeflags & (HFILE_FAST | HFILE_NO_REWRITE))) {
@@ -2314,6 +2411,8 @@ savehistfile(char *fn, int err, int writeflags)
 		pophiststack();
 		histactive = remember_histactive;
 	    }
+	} else {
+	    fclose(out);
 	}
     } else
 	ret = -1;
diff --git a/Src/options.c b/Src/options.c
index a206b2910..9fb3f3388 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -135,6 +135,7 @@ static struct optname optns[] = {
 {{NULL, "histallowclobber",   0},			 HISTALLOWCLOBBER},
 {{NULL, "histbeep",	      OPT_ALL},			 HISTBEEP},
 {{NULL, "histexpiredupsfirst",0},			 HISTEXPIREDUPSFIRST},
+{{NULL, "histfcntllock",      0},			 HISTFCNTLLOCK},
 {{NULL, "histfindnodups",     0},			 HISTFINDNODUPS},
 {{NULL, "histignorealldups",  0},			 HISTIGNOREALLDUPS},
 {{NULL, "histignoredups",     0},			 HISTIGNOREDUPS},
diff --git a/Src/zsh.h b/Src/zsh.h
index 2b8646cb1..67e4c0c31 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1749,6 +1749,7 @@ enum {
     HISTALLOWCLOBBER,
     HISTBEEP,
     HISTEXPIREDUPSFIRST,
+    HISTFCNTLLOCK,
     HISTFINDNODUPS,
     HISTIGNOREALLDUPS,
     HISTIGNOREDUPS,