]> git.wh0rd.org Git - nano.git/commitdiff
2012-12-31 Chris Allegretta <chrisa@asty.org>
authorChris Allegretta <chrisa@asty.org>
Tue, 1 Jan 2013 03:24:39 +0000 (03:24 +0000)
committerChris Allegretta <chrisa@asty.org>
Tue, 1 Jan 2013 03:24:39 +0000 (03:24 +0000)
        * 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

ChangeLog
configure.ac
doc/man/nano.1
doc/man/nanorc.5
doc/syntax/nanorc.nanorc
src/files.c
src/global.c
src/nano.c
src/nano.h
src/proto.h
src/rcfile.c

index b7f0513336635c179de7854b391d52213ca31a1b..5580ba55e9d836beb66fff6ed574d3f576a0a0d4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
        * src/*: Fix overlapping strings highlighting each other.  new variables in edit_draw 
          (slmatcharray, pbegin, paintok), new logic (with repeated setting od values in the 
index c590737c212a190627848ba74af08853300f340c..33199a8a5b835b9fba57f82b9567c5bbdcfba5ce 100644 (file)
@@ -20,7 +20,7 @@
 #
 # $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_CANONICAL_TARGET([])
 AM_INIT_AUTOMAKE
index 37c6eb3f08ed4b5f3619c34e69e2c63c0c34d645..ca6f6ca2b26266a207263b7c06e8d9a5b422c9aa 100644 (file)
@@ -77,6 +77,9 @@ Convert typed tabs to spaces.
 .B \-F (\-\-multibuffer)
 Enable multiple file buffers, if available.
 .TP
+.B \-G (\-\-locking)
+Enable vim-style file locking when editing files.
+.TP
 .B \-H (\-\-historylog)
 Log search and replace strings to \fI~/.nano_history\fP, so they can be
 retrieved in later sessions, if \fInanorc\fP support is available.
index da33179ca449c421fc84e9ddd41b2e2cb80de286..27fc9ecc00c982f7c2d0ae54f897ba6b7f542993 100644 (file)
@@ -102,6 +102,9 @@ default value is \-8.
 Enable \fI~/.nano_history\fP for saving and reading search/replace
 strings.
 .TP
+.B set/unset locking
+Enable vim-style lock-files for when editing files.
+.TP
 .B set matchbrackets "\fIstring\fP"
 Set the opening and closing brackets that can be found by bracket
 searches.  They cannot contain blank characters.  The former set must
index bda5f4f4f444acb2a4a37c33e4e6df55f5206d2e..2c9f23a426b7d7717e80a0c7660a479ad6657c5f 100644 (file)
@@ -4,7 +4,7 @@ syntax "nanorc" "\.?nanorc$"
 ## Possible errors and parameters
 icolor brightwhite "^[[:space:]]*((un)?set|include|syntax|i?color).*$"
 ## 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)\>"
 ## Colors
 icolor yellow "^[[:space:]]*i?color[[:space:]]*(bright)?(white|black|red|blue|green|yellow|magenta|cyan)?(,(white|black|red|blue|green|yellow|magenta|cyan))?\>"
index 382339a6a0b3ca7a0e344bb6fdfbcef6b7c07434..4f3d6b22300622687c6b9830ae0db4c8a001379d 100644 (file)
@@ -31,6 +31,7 @@
 #include <errno.h>
 #include <ctype.h>
 #include <pwd.h>
+#include <libgen.h>
 
 /* Add an entry to the openfile openfilestruct.  This should only be
  * called from open_buffer(). */
@@ -77,6 +78,7 @@ void initialize_buffer(void)
     openfile->current_stat = NULL;
     openfile->undotop = NULL;
     openfile->current_undo = NULL;
+    openfile->lock_filename = NULL;
 #endif
 #ifdef ENABLE_COLOR
     openfile->colorstrings = NULL;
@@ -103,6 +105,197 @@ void initialize_buffer_text(void)
     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
  * necessary, and then open and read the file, if applicable. */
 void open_buffer(const char *filename, bool undoable)
@@ -128,16 +321,16 @@ void open_buffer(const char *filename, bool undoable)
     }
 #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
      * openfile. */
     if (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
      * the filename. */
     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))
        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) {
        /* Well, maybe we can open the file even if the OS
           says its not there */
@@ -1990,6 +2190,7 @@ bool write_marked_file(const char *name, FILE *f_open, bool tmp,
 
     return retval;
 }
+
 #endif /* !NANO_TINY */
 
 /* Write the current file to disk.  If the mark is on, write the current
index b8355db8a515c85dc514b86285b72e2507fb9955..a54f6f92d0b9866b3a9a83a911133f54593553d9 100644 (file)
@@ -130,6 +130,11 @@ ssize_t tabsize = -1;
 #ifndef NANO_TINY
 char *backup_dir = NULL;
        /* 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
 #ifndef DISABLE_OPERATINGDIR
 char *operating_dir = NULL;
index 90c42491936a0933415602b32c5f7624f3b9ad26..ccd6278652b4bf7bcd1cf56ddb1c86cfec104a3e 100644 (file)
@@ -520,6 +520,7 @@ openfilestruct *make_new_opennode(void)
 #ifndef NANO_TINY
     newnode->current_stat = NULL;
     newnode->last_action = OTHER;
+    newnode->lock_filename = NULL;
 #endif
 
     return newnode;
@@ -845,6 +846,8 @@ void usage(void)
 #endif
 #ifdef ENABLE_NANORC
 #ifndef NANO_TINY
+    print_opt("-G", "--locking",
+       N_("Use (vim-style) lock files"));
     print_opt("-H", "--historylog",
        N_("Log & read search/replace string history"));
 #endif
@@ -1058,6 +1061,12 @@ void do_exit(void)
     /* If the user chose not to save, or if the user chose to save and
      * the save succeeded, we're ready to exit. */
     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
        /* Exit only if there are no more open file buffers. */
        if (!close_buffer())
@@ -2098,6 +2107,7 @@ int main(int argc, char **argv)
        {"backup", 0, NULL, 'B'},
        {"backupdir", 1, NULL, 'C'},
        {"tabstospaces", 0, NULL, 'E'},
+       {"locking", 0, NULL, 'G'},
        {"historylog", 0, NULL, 'H'},
        {"noconvert", 0, NULL, 'N'},
        {"poslog", 0, NULL, 'P'},
@@ -2146,11 +2156,11 @@ int main(int argc, char **argv)
     while ((optchr =
 #ifdef HAVE_GETOPT_LONG
        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)
 #else
        getopt(argc, argv,
-               "h?ABC:DEFHIKLNOPQ:RST:UVWY:abcdefgijklmo:pqr:s:tuvwxz$")
+               "h?ABC:DEFGHIKLNOPQ:RST:UVWY:abcdefgijklmo:pqr:s:tuvwxz$")
 #endif
                ) != -1) {
        switch (optchr) {
@@ -2188,6 +2198,9 @@ int main(int argc, char **argv)
 #endif
 #ifdef ENABLE_NANORC
 #ifndef NANO_TINY
+           case 'G':
+               SET(LOCKING);
+               break;
            case 'H':
                SET(HISTORYLOG);
                break;
index 4600d5d52b0fadf05d2a3cb7f21095b897c5817f..28d1d042df37720e9eb45684af461ba2f3b4f5bf 100644 (file)
@@ -375,6 +375,8 @@ typedef struct openfilestruct {
     undo *current_undo;
        /* The current (i.e. n ext) level of undo */
     undo_type last_action;
+    const char *lock_filename;
+        /* The path of the lockfile, if we created one */
 #endif
 #ifdef ENABLE_COLOR
     syntaxtype *syntax;
@@ -510,7 +512,8 @@ enum
     QUIET,
     UNDOABLE,
     SOFTWRAP,
-    POS_HISTORY
+    POS_HISTORY,
+    LOCKING
 };
 
 /* Flags for which menus in which a given function should be present */
index aeb409dbb1f2fc47a549880de0c74717e95368c5..7971fbf808089e68aff90c72aad2d39292bdcc13 100644 (file)
@@ -85,6 +85,8 @@ extern ssize_t tabsize;
 
 #ifndef NANO_TINY
 extern char *backup_dir;
+extern char *locking_prefix;
+extern char *locking_suffix;
 #endif
 #ifndef DISABLE_OPERATINGDIR
 extern char *operating_dir;
@@ -253,6 +255,7 @@ void do_cut_text_void(void);
 #ifndef NANO_TINY
 void do_copy_text(void);
 void do_cut_till_end(void);
+
 #endif
 void do_uncut_text(void);
 
@@ -293,6 +296,7 @@ bool check_operating_dir(const char *currpath, bool allow_tabcomp);
 #endif
 #ifndef NANO_TINY
 void init_backup_dir(void);
+int delete_lockfile(const char *lockfilename);
 #endif
 int copy_file(FILE *inn, FILE *out);
 bool write_file(const char *name, FILE *f_open, bool tmp, append_type
index 8c62cf5b4baad518dc2a23db0c47c22e4fa00f74..56eeb72d594929cad6bc3d4d7d66c3a8a30f8399 100644 (file)
@@ -41,6 +41,9 @@ static const rcoption rcopts[] = {
 #ifndef DISABLE_WRAPJUSTIFY
     {"fill", 0},
 #endif
+#ifndef NANO_TINY
+    {"locking", LOCKING},
+#endif
 #ifndef DISABLE_MOUSE
     {"mouse", USE_MOUSE},
 #endif