From 78ce07b49f08964eaa08d68eb8f6a5595bbcfbc6 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 17 Apr 2008 10:23:45 +0000 Subject: 24818: add HIST_FCNTL_LOCK. unposted: update NEWS with new features --- ChangeLog | 8 ++++ Doc/Zsh/options.yo | 9 +++++ Etc/FAQ.yo | 3 +- NEWS | 16 ++++++++ Src/hist.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++--- Src/options.c | 1 + Src/zsh.h | 1 + 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 + + * 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 * 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, -- cgit 1.4.1