2012-12-31 Chris Allegretta <chrisa@asty.org>

* src/*: Introduce (basic) vim-style file locks.  Does not allow vim to recover
          our files, and doesn't yet support setting the file as modified; just lets a 
          vim user know we're editing a file.  Commands line "-G" or "--locking", nanorc 
          option "locking".  New functions src/files.c:do_lockfile(), write_lockfile(),
          and delete_lockfile().



git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@4549 35c25a1d-7b9e-4130-9fde-d3aeb78583b8
master
Chris Allegretta 2013-01-01 03:24:39 +00:00
parent 2ad1de0ba6
commit bf88d27adc
11 changed files with 252 additions and 10 deletions

View File

@ -1,3 +1,10 @@
2012-12-31 Chris Allegretta <chrisa@asty.org>
* src/*: Introduce (basic) vim-style file locks. Does not allow vim to recover
our files, and doesn't yet support setting the file as modified; just lets a
vim user know we're editing a file. Commands line "-G" or "--locking", nanorc
option "locking". New functions src/files.c:do_lockfile(), write_lockfile(),
and delete_lockfile().
2012-02-05 Chris Allegretta <chrisa@asty.org> 2012-02-05 Chris Allegretta <chrisa@asty.org>
* src/*: Fix overlapping strings highlighting each other. new variables in edit_draw * src/*: Fix overlapping strings highlighting each other. new variables in edit_draw
(slmatcharray, pbegin, paintok), new logic (with repeated setting od values in the (slmatcharray, pbegin, paintok), new logic (with repeated setting od values in the

View File

@ -20,7 +20,7 @@
# #
# $Id$ # $Id$
AC_INIT([GNU nano], [2.3.1], [nano-devel@gnu.org], [nano]) AC_INIT([GNU nano], [2.3.1-svn], [nano-devel@gnu.org], [nano])
AC_CONFIG_SRCDIR([src/nano.c]) AC_CONFIG_SRCDIR([src/nano.c])
AC_CANONICAL_TARGET([]) AC_CANONICAL_TARGET([])
AM_INIT_AUTOMAKE AM_INIT_AUTOMAKE

View File

@ -77,6 +77,9 @@ Convert typed tabs to spaces.
.B \-F (\-\-multibuffer) .B \-F (\-\-multibuffer)
Enable multiple file buffers, if available. Enable multiple file buffers, if available.
.TP .TP
.B \-G (\-\-locking)
Enable vim-style file locking when editing files.
.TP
.B \-H (\-\-historylog) .B \-H (\-\-historylog)
Log search and replace strings to \fI~/.nano_history\fP, so they can be Log search and replace strings to \fI~/.nano_history\fP, so they can be
retrieved in later sessions, if \fInanorc\fP support is available. retrieved in later sessions, if \fInanorc\fP support is available.

View File

@ -102,6 +102,9 @@ default value is \-8.
Enable \fI~/.nano_history\fP for saving and reading search/replace Enable \fI~/.nano_history\fP for saving and reading search/replace
strings. strings.
.TP .TP
.B set/unset locking
Enable vim-style lock-files for when editing files.
.TP
.B set matchbrackets "\fIstring\fP" .B set matchbrackets "\fIstring\fP"
Set the opening and closing brackets that can be found by bracket Set the opening and closing brackets that can be found by bracket
searches. They cannot contain blank characters. The former set must searches. They cannot contain blank characters. The former set must

View File

@ -4,7 +4,7 @@ syntax "nanorc" "\.?nanorc$"
## Possible errors and parameters ## Possible errors and parameters
icolor brightwhite "^[[:space:]]*((un)?set|include|syntax|i?color).*$" icolor brightwhite "^[[:space:]]*((un)?set|include|syntax|i?color).*$"
## Keywords ## Keywords
icolor brightgreen "^[[:space:]]*(set|unset)[[:space:]]+(allow_insecure_backup|autoindent|backup|backupdir|backwards|boldtext|brackets|casesensitive|const|cut|fill|historylog|matchbrackets|morespace|mouse|multibuffer|noconvert|nofollow|nohelp|nonewlines|nowrap|operatingdir|poslog|preserve|punct)\>" "^[[:space:]]*(set|unset)[[:space:]]+(quickblank|quotestr|rebinddelete|rebindkeypad|regexp|smarthome|smooth|softwrap|speller|suspend|suspendenable|tabsize|tabstospaces|tempfile|undo|view|whitespace|wordbounds)\>" icolor brightgreen "^[[:space:]]*(set|unset)[[:space:]]+(allow_insecure_backup|autoindent|backup|backupdir|backwards|boldtext|brackets|casesensitive|const|cut|fill|historylog|matchbrackets|morespace|mouse|multibuffer|noconvert|nofollow|nohelp|nonewlines|nowrap|operatingdir|poslog|preserve|punct)\>" "^[[:space:]]*(set|unset)[[:space:]]+(quickblank|quotestr|rebinddelete|rebindkeypad|regexp|smarthome|smooth|softwrap|speller|suspend|suspendenable|tabsize|tabstospaces|tempfile|undo|view|whitespace|wordbounds|locking)\>"
icolor green "^[[:space:]]*(set|unset|include|syntax|header|magic)\>" icolor green "^[[:space:]]*(set|unset|include|syntax|header|magic)\>"
## Colors ## Colors
icolor yellow "^[[:space:]]*i?color[[:space:]]*(bright)?(white|black|red|blue|green|yellow|magenta|cyan)?(,(white|black|red|blue|green|yellow|magenta|cyan))?\>" icolor yellow "^[[:space:]]*i?color[[:space:]]*(bright)?(white|black|red|blue|green|yellow|magenta|cyan)?(,(white|black|red|blue|green|yellow|magenta|cyan))?\>"

View File

@ -31,6 +31,7 @@
#include <errno.h> #include <errno.h>
#include <ctype.h> #include <ctype.h>
#include <pwd.h> #include <pwd.h>
#include <libgen.h>
/* Add an entry to the openfile openfilestruct. This should only be /* Add an entry to the openfile openfilestruct. This should only be
* called from open_buffer(). */ * called from open_buffer(). */
@ -77,6 +78,7 @@ void initialize_buffer(void)
openfile->current_stat = NULL; openfile->current_stat = NULL;
openfile->undotop = NULL; openfile->undotop = NULL;
openfile->current_undo = NULL; openfile->current_undo = NULL;
openfile->lock_filename = NULL;
#endif #endif
#ifdef ENABLE_COLOR #ifdef ENABLE_COLOR
openfile->colorstrings = NULL; openfile->colorstrings = NULL;
@ -103,6 +105,197 @@ void initialize_buffer_text(void)
openfile->totsize = 0; openfile->totsize = 0;
} }
#ifndef NANO_TINY
/* Actyally write the lock file. This function will
ALWAYS annihilate any previous version of the file.
We'll borrow INSECURE_BACKUP here to decide about lock file
paranoia here as well...
Args:
lockfilename: file name for lock
origfilename: name of the file the lock is for
modified: whether to set the modified bit in the file
Returns: 1 on success, -1 on failure
*/
int write_lockfile(const char *lockfilename, const char *origfilename, bool modified)
{
int cflags, fd;
FILE *filestream;
pid_t mypid;
uid_t myuid;
struct passwd *mypwuid;
char *lockdata = charalloc(1024);
char myhostname[32];
ssize_t lockdatalen = 1024;
ssize_t wroteamt;
/* Run things which might fail first before we try and blow away
the old state */
myuid = geteuid();
if ((mypwuid = getpwuid(myuid)) == NULL) {
statusbar(_("Couldn't determine my identity for lock file (getpwuid() failed)"));
return -1;
}
mypid = getpid();
if (gethostname(myhostname, 31) < 0) {
statusbar(_("Couldn't determine hosttname for lock file: %s"), strerror(errno));
return -1;
}
if (delete_lockfile(lockfilename) < 0)
return -1;
if (ISSET(INSECURE_BACKUP))
cflags = O_WRONLY | O_CREAT | O_APPEND;
else
cflags = O_WRONLY | O_CREAT | O_EXCL | O_APPEND;
fd = open(lockfilename, cflags,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
/* Now we've got a safe file stream. If the previous open()
call failed, this will return NULL. */
filestream = fdopen(fd, "wb");
if (fd < 0 || filestream == NULL) {
statusbar(_("Error writing lock file %s: %s"), lockfilename,
strerror(errno));
return -1;
}
/* Okay. so at the moment we're following this state for how
to store the lock data:
byte 0 - 0x62
byte 1 - 0x30
bytes 2-12 - program name which created the lock
bytes 24,25 - little endian store of creator program's PID
(b24 = 256^0 column, b25 = 256^1 column)
bytes 28-44 - username of who created the lock
bytes 68-100 - hostname of where the lock was created
bytes 108-876 - filename the lock is for
byte 1018 - 0x55 if file is modified
(TODO: set if 'modified' == TRUE)
Looks like VIM also stores undo state in this file so we're
gonna have to figure out how to slap a 'OMG don't use recover
on our lockfile' message in here...
This is likely very wrong, so this is a WIP
*/
null_at(&lockdata, lockdatalen);
lockdata[0] = 0x62;
lockdata[1] = 0x30;
lockdata[24] = mypid % 256;
lockdata[25] = mypid / 256;
snprintf(&lockdata[2], 10, "nano %s", VERSION);
strncpy(&lockdata[28], mypwuid->pw_name, 16);
strncpy(&lockdata[68], myhostname, 31);
strncpy(&lockdata[108], origfilename, 768);
wroteamt = fwrite(lockdata, sizeof(char), lockdatalen, filestream);
if (wroteamt < lockdatalen) {
statusbar(_("Error writing lock file %s: %s"),
lockfilename, ferror(filestream));
return -1;
}
#ifdef DEBUG
fprintf(stderr, "In write_lockfile(), write successful (wrote %d bytes)\n", wroteamt);
#endif /* DEBUG */
if (fclose(filestream) == EOF) {
statusbar(_("Error writing lock file %s: %s"),
lockfilename, strerror(errno));
return -1;
}
openfile->lock_filename = lockfilename;
return 1;
}
/* Less exciting, delete the lock file.
Return -1 if successful and complain on the statusbar, 1 otherwite
*/
int delete_lockfile(const char *lockfilename)
{
if (unlink(lockfilename) < 0 && errno != ENOENT) {
statusbar(_("Error deleting lock file %s: %s"), lockfilename,
strerror(errno));
return -1;
}
return 1;
}
/* Deal with lockfiles. Return -1 on refusing to override
the lock file, and 1 on successfully created the lockfile.
*/
int do_lockfile(const char *filename)
{
char *lockdir = dirname((char *) mallocstrcpy(NULL, filename));
char *lockbase = basename((char *) mallocstrcpy(NULL, filename));
ssize_t lockfilesize = (sizeof (char *) * (strlen(filename)
+ strlen(locking_prefix) + strlen(locking_suffix) + 3));
char *lockfilename = nmalloc(lockfilesize);
char lockprog[12], lockuser[16];
struct stat fileinfo;
int lockfd, lockpid;
snprintf(lockfilename, lockfilesize, "%s/%s%s%s", lockdir,
locking_prefix, lockbase, locking_suffix);
#ifdef DEBUG
fprintf(stderr, "lock file name is %s\n", lockfilename);
#endif /* DEBUG */
if (stat(lockfilename, &fileinfo) != -1) {
ssize_t readtot = 0;
ssize_t readamt = 0;
char *lockbuf = nmalloc(8192);
char *promptstr = nmalloc(128);
int ans;
if ((lockfd = open(lockfilename, O_RDONLY)) < 0) {
statusbar(_("Error opening lockfile %s: %s"),
lockfilename, strerror(errno));
return -1;
}
do {
readamt = read(lockfd, &lockbuf[readtot], BUFSIZ);
readtot += readamt;
} while (readtot < 8192 && readamt > 0);
if (readtot < 48) {
statusbar(_("Error reading lockfile %s: Not enough data read"),
lockfilename);
return -1;
}
strncpy(lockprog, &lockbuf[2], 10);
lockpid = lockbuf[25] * 256 + lockbuf[24];
strncpy(lockuser, &lockbuf[28], 16);
#ifdef DEBUG
fprintf(stderr, "lockpid = %d\n", lockpid);
fprintf(stderr, "program name which created this lock file should be %s\n",
lockprog);
fprintf(stderr, "user which created this lock file should be %s\n",
lockuser);
#endif /* DEBUG */
sprintf(promptstr, "File being edited (by %s, PID %d, user %s), continue?",
lockprog, lockpid, lockuser);
ans = do_yesno_prompt(FALSE, promptstr);
if (ans < 1) {
blank_statusbar();
return -1;
}
}
return write_lockfile(lockfilename, filename, FALSE);
}
#endif /* NANO_TINY */
/* If it's not "", filename is a file to open. We make a new buffer, if /* If it's not "", filename is a file to open. We make a new buffer, if
* necessary, and then open and read the file, if applicable. */ * necessary, and then open and read the file, if applicable. */
void open_buffer(const char *filename, bool undoable) void open_buffer(const char *filename, bool undoable)
@ -128,16 +321,16 @@ void open_buffer(const char *filename, bool undoable)
} }
#endif #endif
/* If the filename isn't blank, open the file. Otherwise, treat it
* as a new file. */
rc = (filename[0] != '\0') ? open_file(filename, new_buffer, &f) :
-2;
/* If we're loading into a new buffer, add a new entry to /* If we're loading into a new buffer, add a new entry to
* openfile. */ * openfile. */
if (new_buffer) if (new_buffer)
make_new_buffer(); make_new_buffer();
/* If the filename isn't blank, open the file. Otherwise, treat it
* as a new file. */
rc = (filename[0] != '\0') ? open_file(filename, new_buffer, &f) :
-2;
/* If we have a file, and we're loading into a new buffer, update /* If we have a file, and we're loading into a new buffer, update
* the filename. */ * the filename. */
if (rc != -1 && new_buffer) if (rc != -1 && new_buffer)
@ -697,6 +890,13 @@ int open_file(const char *filename, bool newfie, FILE **f)
|| (stat(full_filename, &fileinfo) == -1 && stat(filename, &fileinfo2) != -1)) || (stat(full_filename, &fileinfo) == -1 && stat(filename, &fileinfo2) != -1))
full_filename = mallocstrcpy(NULL, filename); full_filename = mallocstrcpy(NULL, filename);
#ifndef NANO_TINY
if (ISSET(LOCKING))
if (do_lockfile(full_filename) < 0)
return -1;
#endif
if (stat(full_filename, &fileinfo) == -1) { if (stat(full_filename, &fileinfo) == -1) {
/* Well, maybe we can open the file even if the OS /* Well, maybe we can open the file even if the OS
says its not there */ says its not there */
@ -1990,6 +2190,7 @@ bool write_marked_file(const char *name, FILE *f_open, bool tmp,
return retval; return retval;
} }
#endif /* !NANO_TINY */ #endif /* !NANO_TINY */
/* Write the current file to disk. If the mark is on, write the current /* Write the current file to disk. If the mark is on, write the current

View File

@ -130,6 +130,11 @@ ssize_t tabsize = -1;
#ifndef NANO_TINY #ifndef NANO_TINY
char *backup_dir = NULL; char *backup_dir = NULL;
/* The directory where we store backup files. */ /* The directory where we store backup files. */
char *locking_prefix = ".";
/* Prefix of how to store the vim-style lock file */
char *locking_suffix = ".swp";
/* Suffix of the vim-style lock file */
#endif #endif
#ifndef DISABLE_OPERATINGDIR #ifndef DISABLE_OPERATINGDIR
char *operating_dir = NULL; char *operating_dir = NULL;

View File

@ -520,6 +520,7 @@ openfilestruct *make_new_opennode(void)
#ifndef NANO_TINY #ifndef NANO_TINY
newnode->current_stat = NULL; newnode->current_stat = NULL;
newnode->last_action = OTHER; newnode->last_action = OTHER;
newnode->lock_filename = NULL;
#endif #endif
return newnode; return newnode;
@ -845,6 +846,8 @@ void usage(void)
#endif #endif
#ifdef ENABLE_NANORC #ifdef ENABLE_NANORC
#ifndef NANO_TINY #ifndef NANO_TINY
print_opt("-G", "--locking",
N_("Use (vim-style) lock files"));
print_opt("-H", "--historylog", print_opt("-H", "--historylog",
N_("Log & read search/replace string history")); N_("Log & read search/replace string history"));
#endif #endif
@ -1058,6 +1061,12 @@ void do_exit(void)
/* If the user chose not to save, or if the user chose to save and /* If the user chose not to save, or if the user chose to save and
* the save succeeded, we're ready to exit. */ * the save succeeded, we're ready to exit. */
if (i == 0 || (i == 1 && do_writeout(TRUE))) { if (i == 0 || (i == 1 && do_writeout(TRUE))) {
#ifndef NANO_TINY
if (ISSET(LOCKING) && openfile->lock_filename)
delete_lockfile(openfile->lock_filename);
#endif /* NANO_TINY */
#ifdef ENABLE_MULTIBUFFER #ifdef ENABLE_MULTIBUFFER
/* Exit only if there are no more open file buffers. */ /* Exit only if there are no more open file buffers. */
if (!close_buffer()) if (!close_buffer())
@ -2098,6 +2107,7 @@ int main(int argc, char **argv)
{"backup", 0, NULL, 'B'}, {"backup", 0, NULL, 'B'},
{"backupdir", 1, NULL, 'C'}, {"backupdir", 1, NULL, 'C'},
{"tabstospaces", 0, NULL, 'E'}, {"tabstospaces", 0, NULL, 'E'},
{"locking", 0, NULL, 'G'},
{"historylog", 0, NULL, 'H'}, {"historylog", 0, NULL, 'H'},
{"noconvert", 0, NULL, 'N'}, {"noconvert", 0, NULL, 'N'},
{"poslog", 0, NULL, 'P'}, {"poslog", 0, NULL, 'P'},
@ -2146,11 +2156,11 @@ int main(int argc, char **argv)
while ((optchr = while ((optchr =
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
getopt_long(argc, argv, getopt_long(argc, argv,
"h?ABC:DEFHIKLNOPQ:RST:UVWY:abcdefgijklmo:pqr:s:tuvwxz$", "h?ABC:DEFGHIKLNOPQ:RST:UVWY:abcdefgijklmo:pqr:s:tuvwxz$",
long_options, NULL) long_options, NULL)
#else #else
getopt(argc, argv, getopt(argc, argv,
"h?ABC:DEFHIKLNOPQ:RST:UVWY:abcdefgijklmo:pqr:s:tuvwxz$") "h?ABC:DEFGHIKLNOPQ:RST:UVWY:abcdefgijklmo:pqr:s:tuvwxz$")
#endif #endif
) != -1) { ) != -1) {
switch (optchr) { switch (optchr) {
@ -2188,6 +2198,9 @@ int main(int argc, char **argv)
#endif #endif
#ifdef ENABLE_NANORC #ifdef ENABLE_NANORC
#ifndef NANO_TINY #ifndef NANO_TINY
case 'G':
SET(LOCKING);
break;
case 'H': case 'H':
SET(HISTORYLOG); SET(HISTORYLOG);
break; break;

View File

@ -375,6 +375,8 @@ typedef struct openfilestruct {
undo *current_undo; undo *current_undo;
/* The current (i.e. n ext) level of undo */ /* The current (i.e. n ext) level of undo */
undo_type last_action; undo_type last_action;
const char *lock_filename;
/* The path of the lockfile, if we created one */
#endif #endif
#ifdef ENABLE_COLOR #ifdef ENABLE_COLOR
syntaxtype *syntax; syntaxtype *syntax;
@ -510,7 +512,8 @@ enum
QUIET, QUIET,
UNDOABLE, UNDOABLE,
SOFTWRAP, SOFTWRAP,
POS_HISTORY POS_HISTORY,
LOCKING
}; };
/* Flags for which menus in which a given function should be present */ /* Flags for which menus in which a given function should be present */

View File

@ -85,6 +85,8 @@ extern ssize_t tabsize;
#ifndef NANO_TINY #ifndef NANO_TINY
extern char *backup_dir; extern char *backup_dir;
extern char *locking_prefix;
extern char *locking_suffix;
#endif #endif
#ifndef DISABLE_OPERATINGDIR #ifndef DISABLE_OPERATINGDIR
extern char *operating_dir; extern char *operating_dir;
@ -253,6 +255,7 @@ void do_cut_text_void(void);
#ifndef NANO_TINY #ifndef NANO_TINY
void do_copy_text(void); void do_copy_text(void);
void do_cut_till_end(void); void do_cut_till_end(void);
#endif #endif
void do_uncut_text(void); void do_uncut_text(void);
@ -293,6 +296,7 @@ bool check_operating_dir(const char *currpath, bool allow_tabcomp);
#endif #endif
#ifndef NANO_TINY #ifndef NANO_TINY
void init_backup_dir(void); void init_backup_dir(void);
int delete_lockfile(const char *lockfilename);
#endif #endif
int copy_file(FILE *inn, FILE *out); int copy_file(FILE *inn, FILE *out);
bool write_file(const char *name, FILE *f_open, bool tmp, append_type bool write_file(const char *name, FILE *f_open, bool tmp, append_type

View File

@ -41,6 +41,9 @@ static const rcoption rcopts[] = {
#ifndef DISABLE_WRAPJUSTIFY #ifndef DISABLE_WRAPJUSTIFY
{"fill", 0}, {"fill", 0},
#endif #endif
#ifndef NANO_TINY
{"locking", LOCKING},
#endif
#ifndef DISABLE_MOUSE #ifndef DISABLE_MOUSE
{"mouse", USE_MOUSE}, {"mouse", USE_MOUSE},
#endif #endif