Mercurial > emacs
view src/filelock.c @ 4872:628cbf7e7005
(comint-after-partial-file-name-command):
Renamed from comint-after-partial-pathname-command.
(comint-match-partial-file-name, comint-after-partial-file-name):
Renamed from comint-match-partial-pathname, etc.
(comint-last-output-start): New variable to record
where most recent process output started from.
(comint-mode): Initialise it.
(comint-output-filter): Set it.
(comint-previous-matching-input-string): Moved to
comint-previous-matching-input-position.
(comint-previous-matching-input-string): Use it.
(comint-search-arg, comint-search-start,
comint-previous-input-string): New subroutines.
(comint-previous-input, comint-next-input,
comint-previous-matching-input, comint-next-matching-input,
comint-previous-matching-input-from-input,
comint-next-matching-input-from-input): Use them.
(comint-mode-map): Added signal menu-bar. Moved
comint-backward/forward-matching-input to output menu-bar, since
they move within the buffer rather than do input.
(comint-send-input, comint-after-pmark-p,
comint-kill-input, comint-proc-query): Removed serialisation of
obtaining the process mark's marker-position.
Commented out comint-load-hooks.
(comint-dynamic-simple-complete): New subroutine.
(comint-dynamic-complete-filename-command): New variable.
(comint-after-partial-pathname-command): New variable.
(comint-after-partial-pathname): New subroutine.
(comint-dynamic-complete): Use them.
(comint-mode): Make them local.
Renamed comint-dynamic-complete-command to
comint-dynamic-complete-command-command for consistency.
Renamed comint-file-name-addsuffix/autolist/recexact to
comint-completion-addsuffix/autolist/recexact for consistency.
(comint-replace-by-expanded-history): Check if input
ring size is not big enough for relative reference.
(comint-read-input-ring, comint-input-ring-file-name):
From shell.el.
(shell-write-input-ring): New subroutine.
(comint-file-name-prefix): New variable.
(comint-directory): New inline subroutine.
(comint-dynamic-complete-filename, comint-dynamic-complete-variable,
comint-dynamic-list-filename-completions): Use it.
(comint-dynamic-complete-filename,
comint-dynamic-complete-variable,
comint-dynamic-list-filename-completions): Make sure local
completion-ignore-case is nil.
(comint-next-prompt, comint-previous-prompt): Use
paragraph-start and paragraph motion commands rather than
re-search-forward and re-search-backward commands.
(comint-dynamic-list-input-ring,
comint-previous-matching-input-string): Use ring-empty-p rather
than zerop and ring-length.
(comint-input-ignoredups): New variable.
(comint-send-input, shell-read-input-ring): Use it.
(comint-mode): Make comint-input-ignoredups local. Doc fix.
(comint-scroll-to-bottom-on-input): New variable.
(comint-scroll-to-bottom-on-output): New variable.
(comint-scroll-show-maximum-output): New variable.
(comint-output-filter-hook): New variable, defaults to
comint-postoutput-scroll-to-bottom.
(comint-output-filter): Renamed from comint-filter
for consistency. Now calls comint-output-filter-hook.
(comint-preinput-scroll-to-bottom): New subroutine.
(comint-postoutput-scroll-to-bottom): New subroutine.
(comint-show-maximum-output): New command.
(comint-copy-old-input): New command.
(comint-send-input): Run comint-output-filter-hook if
necessary as a kludge to prevent messy redisplays.
(comint-mode-map): Added comint-show-maximum-output to
C-c C-e and menu-bar output, and comint-copy-old-input to C-c C-i
and menu-bar input.
(comint-mode): Make local variables
comint-scroll-to-bottom-on-input, before-change-function,
comint-scroll-to-bottom-on-output, comint-scroll-show-maximum-output,
and comint-output-filter-hook.
(comint-version): Deleted--no need for separate version.
(comint-input-ring-index): Make this a permanent local.
(comint-mode): Don't alter comint-input-ring-index or comint-input-ring
if already set meaningfully.
(comint-mode-map): Added keys M-R/S for
comint-previous/next-matching-input-from-input and to completion
menu-bar. Added comint-forward/backward-matching-input and
comint-previous/next-matching-input to completion menu-bar.
(comint-mode): Doc fix for functionality.
(comint-exec-1): Uses setenv.
(comint-update-env): Removed.
(comint-input-ring-size): Incremented to 32, as with
command history.
(comint-dynamic-list-input-ring): Check for zero
length ring. Use ring length, not ring size, when generating
list. Use buffer " *Input History*".
(comint-previous-matching-input-string): Check for zero-length ring.
Check last item in case at end of cycle and it's a match.
(comint-searching-input-ring): New subroutine.
(comint-regexp-arg): New subroutine.
(comint-previous-matching-input-from-input): New command.
(comint-next-matching-input-from-input): New command.
(comint-replace-by-expanded-history): Fix for matching
inside quotes. Fix to allow argument subrange specifiers. Fix to
identify and reject absolute input number references.
(comint-within-quotes): New subroutine.
(comint-how-many-region): New subroutine.
(comint-args): New subroutine.
(comint-delim-arg): New subroutine.
(comint-arguments): New subroutine.
(comint-delimiter-argument-list): New variable.
(comint-send-input): Inserts input arguments into ring
separated by single spaces.
(comint-filter): Checks the buffer's process to make
sure it's still there. Otherwise, set-buffer will fail.
(comint-backward-matching-input): New command.
(comint-forward-matching-input): New command.
(comint-next-prompt, comint-previous-prompt):
Error if reach beg/end of buffer.
(comint-dynamic-complete): Fix for absolute input
number references.
(comint-dynamic-complete-filename): Changed listings
function to comint-dynamic-list-filename-completions. Uses
file-directory-p rather than string-match to test for directories.
(comint-dynamic-list-completions): Changed to list the
list of completions supplied as the function argument. Use buffer
" *Completions*".
(comint-match-partial-pathname): New subroutine.
(comint-dynamic-complete-variable): New command.
(comint-dynamic-list-filename-completions): New function.
(comint-previous-input):
Don't use replace-match; just insert before deleting.
(comint-magic-space): Use self-insert command.
(comint-history-file-name): New variable.
(comint-mode): Initialize comint-input-ring before
running comint-mode-hook.
(comint-input-autoexpand): New variable.
(comint-dynamic-complete-command): New variable.
(comint-get-current-command): New variable.
(comint-read-input-ring): New function.
(comint-send-input): Handle history expansion.
(comint-input-sentinel): Doc fix.
(comint-mode-map): Added key binding for C-c C-h.
Added menu bars for completion, input and output.
(comint-dynamic-list-input-ring): New function.
(comint-previous-input-string): New subroutine.
(comint-previous-input): Use it.
(comint-previous-matching-input-string): New subroutine.
(comint-previous-matching-input): Use it.
(comint-replace-by-expanded-history): New command.
(comint-magic-space): New command.
(comint-replace-by-expanded-filename): Now replaces
expanded match for a filename, and then calls filename completion
comint-dynamic-complete-filename to do file name completion.
(comint-kill-output): Don't kill prompt.
(comint-show-output): Don't move point if it's
visible where it is, and if point is moved, put it after prompt.
(comint-dynamic-complete): Totally new definition.
(comint-dynamic-complete-filename): New name for old
function comint-dynamic-complete, completes files and lists
candidates, souped up for configurability.
(comint-dynamic-complete-variable): New command.
(comint-file-name-autolist): New variable.
(comint-file-name-addsuffix): New variable,
(comint-file-name-recexact): New variable.
author | Richard M. Stallman <rms@gnu.org> |
---|---|
date | Fri, 22 Oct 1993 02:57:36 +0000 |
parents | 05f6a91c2801 |
children | 0aa51282fbfe |
line wrap: on
line source
/* Copyright (C) 1985, 1986, 1987, 1993 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <sys/types.h> #include <sys/stat.h> #include <config.h> #ifdef VMS #include "vms-pwd.h" #else #include <pwd.h> #endif #include <errno.h> #include <sys/file.h> #ifdef USG #include <fcntl.h> #endif /* USG */ #include "lisp.h" #include <paths.h> #include "buffer.h" extern int errno; extern char *egetenv (); extern char *strcpy (); #if defined (__bsdi__) || defined (DECLARE_GETPWUID_WITH_UID_T) extern struct passwd *getpwuid (uid_t); #else extern struct passwd *getpwuid (); #endif #ifdef CLASH_DETECTION /* If system does not have symbolic links, it does not have lstat. In that case, use ordinary stat instead. */ #ifndef S_IFLNK #define lstat stat #endif /* The name of the directory in which we keep lock files, with a '/' appended. */ char *lock_path; /* The name of the file in the lock directory which is used to arbitrate access to the entire directory. */ #define SUPERLOCK_NAME "!!!SuperLock!!!" /* The path to the superlock file. This is SUPERLOCK_NAME appended to lock_path. */ char *superlock_path; /* Set LOCK to the name of the lock file for the filename FILE. char *LOCK; Lisp_Object FILE; */ #ifndef HAVE_LONG_FILE_NAMES #define MAKE_LOCK_PATH(lock, file) \ (lock = (char *) alloca (14 + strlen (lock_path) + 1), \ fill_in_lock_short_file_name (lock, (file))) fill_in_lock_short_file_name (lockfile, fn) register char *lockfile; register Lisp_Object fn; { register union { unsigned int word [2]; unsigned char byte [8]; } crc; register unsigned char *p, new; /* 7-bytes cyclic code for burst correction on byte-by-byte basis. the used polynomial is D^7 + D^6 + D^3 +1. pot@cnuce.cnr.it */ crc.word[0] = crc.word[1] = 0; for (p = XSTRING (fn)->data; new = *p++; ) { new += crc.byte[6]; crc.byte[6] = crc.byte[5] + new; crc.byte[5] = crc.byte[4]; crc.byte[4] = crc.byte[3]; crc.byte[3] = crc.byte[2] + new; crc.byte[2] = crc.byte[1]; crc.byte[1] = crc.byte[0]; crc.byte[0] = new; } sprintf (lockfile, "%s%.2x%.2x%.2x%.2x%.2x%.2x%.2x", lock_path, crc.byte[0], crc.byte[1], crc.byte[2], crc.byte[3], crc.byte[4], crc.byte[5], crc.byte[6]); } #else /* defined HAVE_LONG_FILE_NAMES */ #define MAKE_LOCK_PATH(lock, file) \ (lock = (char *) alloca (XSTRING (file)->size + strlen (lock_path) + 1), \ fill_in_lock_file_name (lock, (file))) fill_in_lock_file_name (lockfile, fn) register char *lockfile; register Lisp_Object fn; { register char *p; strcpy (lockfile, lock_path); p = lockfile + strlen (lockfile); strcpy (p, XSTRING (fn)->data); for (; *p; p++) { if (*p == '/') *p = '!'; } } #endif /* !defined HAVE_LONG_FILE_NAMES */ static Lisp_Object lock_file_owner_name (lfname) char *lfname; { struct stat s; struct passwd *the_pw; if (lstat (lfname, &s) == 0) the_pw = getpwuid (s.st_uid); return (the_pw == 0 ? Qnil : build_string (the_pw->pw_name)); } /* lock_file locks file fn, meaning it serves notice on the world that you intend to edit that file. This should be done only when about to modify a file-visiting buffer previously unmodified. Do not (normally) call lock_buffer for a buffer already modified, as either the file is already locked, or the user has already decided to go ahead without locking. When lock_buffer returns, either the lock is locked for us, or the user has said to go ahead without locking. If the file is locked by someone else, lock_buffer calls ask-user-about-lock (a Lisp function) with two arguments, the file name and the name of the user who did the locking. This function can signal an error, or return t meaning take away the lock, or return nil meaning ignore the lock. */ /* The lock file name is the file name with "/" replaced by "!" and put in the Emacs lock directory. */ /* (ie., /ka/king/junk.tex -> /!/!ka!king!junk.tex). */ /* If HAVE_LONG_FILE_NAMES is not defined, the lock file name is the hex representation of a 14-bytes CRC generated from the file name and put in the Emacs lock directory (not very nice, but it works). (ie., /ka/king/junk.tex -> /!/12a82c62f1c6da). */ void lock_file (fn) register Lisp_Object fn; { register Lisp_Object attack; register char *lfname; MAKE_LOCK_PATH (lfname, fn); /* See if this file is visited and has changed on disk since it was visited. */ { register Lisp_Object subject_buf = Fget_file_buffer (fn); if (!NILP (subject_buf) && NILP (Fverify_visited_file_modtime (subject_buf)) && !NILP (Ffile_exists_p (fn))) call1 (intern ("ask-user-about-supersession-threat"), fn); } /* Try to lock the lock. */ if (lock_if_free (lfname) <= 0) /* Return now if we have locked it, or if lock dir does not exist */ return; /* Else consider breaking the lock */ attack = call2 (intern ("ask-user-about-lock"), fn, lock_file_owner_name (lfname)); if (!NILP (attack)) /* User says take the lock */ { lock_superlock (lfname); lock_file_1 (lfname, O_WRONLY) ; unlink (superlock_path); return; } /* User says ignore the lock */ } /* Lock the lock file named LFNAME. If MODE is O_WRONLY, we do so even if it is already locked. If MODE is O_WRONLY | O_EXCL | O_CREAT, we do so only if it is free. Return 1 if successful, 0 if not. */ int lock_file_1 (lfname, mode) int mode; char *lfname; { register int fd; char buf[20]; if ((fd = open (lfname, mode, 0666)) >= 0) { #ifdef USG chmod (lfname, 0666); #else fchmod (fd, 0666); #endif sprintf (buf, "%d ", getpid ()); write (fd, buf, strlen (buf)); close (fd); return 1; } else return 0; } /* Lock the lock named LFNAME if possible. Return 0 in that case. Return positive if lock is really locked by someone else. Return -1 if cannot lock for any other reason. */ int lock_if_free (lfname) register char *lfname; { register int clasher; while (lock_file_1 (lfname, O_WRONLY | O_EXCL | O_CREAT) == 0) { if (errno != EEXIST) return -1; clasher = current_lock_owner (lfname); if (clasher != 0) if (clasher != getpid ()) return (clasher); else return (0); /* Try again to lock it */ } return 0; } /* Return the pid of the process that claims to own the lock file LFNAME, or 0 if nobody does or the lock is obsolete, or -1 if something is wrong with the locking mechanism. */ int current_lock_owner (lfname) char *lfname; { int owner = current_lock_owner_1 (lfname); if (owner == 0 && errno == ENOENT) return (0); /* Is it locked by a process that exists? */ if (owner != 0 && (kill (owner, 0) >= 0 || errno == EPERM)) return (owner); if (unlink (lfname) < 0) return (-1); return (0); } int current_lock_owner_1 (lfname) char *lfname; { register int fd; char buf[20]; int tem; fd = open (lfname, O_RDONLY, 0666); if (fd < 0) return 0; tem = read (fd, buf, sizeof buf); close (fd); return (tem <= 0 ? 0 : atoi (buf)); } void unlock_file (fn) register Lisp_Object fn; { register char *lfname; MAKE_LOCK_PATH (lfname, fn); lock_superlock (lfname); if (current_lock_owner_1 (lfname) == getpid ()) unlink (lfname); unlink (superlock_path); } lock_superlock (lfname) char *lfname; { register int i, fd; for (i = -20; i < 0 && (fd = open (superlock_path, O_WRONLY | O_EXCL | O_CREAT, 0666)) < 0; i++) { if (errno != EEXIST) return; sleep (1); } if (fd >= 0) { #ifdef USG chmod (superlock_path, 0666); #else fchmod (fd, 0666); #endif write (fd, lfname, strlen (lfname)); close (fd); } } void unlock_all_files () { register Lisp_Object tail; register struct buffer *b; for (tail = Vbuffer_alist; XGCTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr) { b = XBUFFER (XCONS (XCONS (tail)->car)->cdr); if (XTYPE (b->filename) == Lisp_String && b->save_modified < BUF_MODIFF (b)) unlock_file (b->filename); } } DEFUN ("lock-buffer", Flock_buffer, Slock_buffer, 0, 1, 0, "Lock FILE, if current buffer is modified.\n\ FILE defaults to current buffer's visited file,\n\ or else nothing is done if current buffer isn't visiting a file.") (fn) Lisp_Object fn; { if (NILP (fn)) fn = current_buffer->filename; else CHECK_STRING (fn, 0); if (current_buffer->save_modified < MODIFF && !NILP (fn)) lock_file (fn); return Qnil; } DEFUN ("unlock-buffer", Funlock_buffer, Sunlock_buffer, 0, 0, 0, "Unlock the file visited in the current buffer,\n\ if it should normally be locked.") () { if (current_buffer->save_modified < MODIFF && XTYPE (current_buffer->filename) == Lisp_String) unlock_file (current_buffer->filename); return Qnil; } /* Unlock the file visited in buffer BUFFER. */ unlock_buffer (buffer) struct buffer *buffer; { if (buffer->save_modified < BUF_MODIFF (buffer) && XTYPE (buffer->filename) == Lisp_String) unlock_file (buffer->filename); } DEFUN ("file-locked-p", Ffile_locked_p, Sfile_locked_p, 0, 1, 0, "Return nil if the FILENAME is not locked,\n\ t if it is locked by you, else a string of the name of the locker.") (fn) Lisp_Object fn; { register char *lfname; int owner; fn = Fexpand_file_name (fn, Qnil); MAKE_LOCK_PATH (lfname, fn); owner = current_lock_owner (lfname); if (owner <= 0) return (Qnil); else if (owner == getpid ()) return (Qt); return (lock_file_owner_name (lfname)); } /* Initialization functions. */ init_filelock () { lock_path = egetenv ("EMACSLOCKDIR"); if (! lock_path) lock_path = PATH_LOCK; /* Make sure it ends with a slash. */ if (lock_path[strlen (lock_path) - 1] != '/') { char *new_path = (char *) xmalloc (strlen (lock_path) + 2); strcpy (new_path, lock_path); lock_path = new_path; strcat (lock_path, "/"); } superlock_path = (char *) xmalloc ((strlen (lock_path) + sizeof (SUPERLOCK_NAME))); strcpy (superlock_path, lock_path); strcat (superlock_path, SUPERLOCK_NAME); } syms_of_filelock () { defsubr (&Sunlock_buffer); defsubr (&Slock_buffer); defsubr (&Sfile_locked_p); } #endif /* CLASH_DETECTION */