Fix performance bug on hosts with large
[bpt/emacs.git] / src / filelock.c
CommitLineData
b97771fc
RS
1/* Lock files for editing.
2 Copyright (C) 1985, 86, 87, 93, 94, 96, 98, 1999 Free Software Foundation, Inc.
8489eb67
RS
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
32676c08 8the Free Software Foundation; either version 2, or (at your option)
8489eb67
RS
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
3b7ad313
EN
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
8489eb67
RS
20
21
22#include <sys/types.h>
23#include <sys/stat.h>
dfcf069d 24#include <signal.h>
18160b98 25#include <config.h>
bfb61299
JB
26
27#ifdef VMS
b350a838 28#include "vms-pwd.h"
bfb61299 29#else
8489eb67 30#include <pwd.h>
8dbbc384 31#endif /* not VMS */
bfb61299 32
8489eb67
RS
33#include <sys/file.h>
34#ifdef USG
35#include <fcntl.h>
8dbbc384 36#include <string.h>
8489eb67
RS
37#endif /* USG */
38
dfcf069d
AS
39#ifdef HAVE_UNISTD_H
40#include <unistd.h>
41#endif
42
f805a125
KH
43#ifdef __FreeBSD__
44#include <sys/time.h>
45#include <sys/types.h>
46#include <sys/sysctl.h>
47#endif /* __FreeBSD__ */
48
8489eb67 49#include "lisp.h"
8489eb67 50#include "buffer.h"
f4a4528d
RS
51#include "charset.h"
52#include "coding.h"
9177d978 53#include "systime.h"
8489eb67 54
15e88d21 55#include <time.h>
8dbbc384
RS
56#include <errno.h>
57#ifndef errno
8489eb67 58extern int errno;
79941276
RS
59#endif
60
8489eb67 61#ifdef CLASH_DETECTION
e788eecc
KH
62
63#include <utmp.h>
77e544a4
RS
64
65#ifndef WTMP_FILE
66#define WTMP_FILE "/var/log/wtmp"
67#endif
8489eb67 68
8dbbc384
RS
69/* The strategy: to lock a file FN, create a symlink .#FN in FN's
70 directory, with link data `user@host.pid'. This avoids a single
71 mount (== failure) point for lock files.
72
73 When the host in the lock data is the current host, we can check if
74 the pid is valid with kill.
75
76 Otherwise, we could look at a separate file that maps hostnames to
77 reboot times to see if the remote pid can possibly be valid, since we
78 don't want Emacs to have to communicate via pipes or sockets or
79 whatever to other processes, either locally or remotely; rms says
80 that's too unreliable. Hence the separate file, which could
81 theoretically be updated by daemons running separately -- but this
82 whole idea is unimplemented; in practice, at least in our
1c4f857c 83 environment, it seems such stale locks arise fairly infrequently, and
8dbbc384
RS
84 Emacs' standard methods of dealing with clashes suffice.
85
86 We use symlinks instead of normal files because (1) they can be
87 stored more efficiently on the filesystem, since the kernel knows
88 they will be small, and (2) all the info about the lock can be read
89 in a single system call (readlink). Although we could use regular
1c4f857c 90 files to be useful on old systems lacking symlinks, nowadays
8dbbc384
RS
91 virtually all such systems are probably single-user anyway, so it
92 didn't seem worth the complication.
93
94 Similarly, we don't worry about a possible 14-character limit on
95 file names, because those are all the same systems that don't have
96 symlinks.
97
98 This is compatible with the locking scheme used by Interleaf (which
99 has contributed this implementation for Emacs), and was designed by
100 Ethan Jacobson, Kimbo Mundy, and others.
101
102 --karl@cs.umb.edu/karl@hq.ileaf.com. */
8489eb67 103
8dbbc384 104\f
15e88d21
RS
105/* Return the time of the last system boot. */
106
107static time_t boot_time;
b97771fc 108static int boot_time_initialized;
15e88d21 109
9177d978
RS
110extern Lisp_Object Vshell_file_name;
111
15e88d21
RS
112static time_t
113get_boot_time ()
114{
115 struct utmp ut, *utp;
9177d978
RS
116 int fd;
117 EMACS_TIME time_before, after;
118 int counter;
15e88d21 119
b97771fc 120 if (boot_time_initialized)
15e88d21 121 return boot_time;
b97771fc 122 boot_time_initialized = 1;
15e88d21 123
9177d978
RS
124 EMACS_GET_TIME (time_before);
125
126 /* Try calculating the last boot time
127 from the uptime as obtained from /proc/uptime. */
128
129 while ((fd = open ("/proc/uptime", O_RDONLY)) >= 0)
130 {
8e339303 131 char buf[100];
9177d978
RS
132 int res;
133 double upsecs;
134 time_t uptime;
135
8e339303 136 read (fd, buf, sizeof buf);
9177d978
RS
137 close (fd);
138
139 res = sscanf (buf, "%lf", &upsecs);
140
141 /* If the current time did not tick while we were getting the
142 uptime, we have a valid result. */
143 EMACS_GET_TIME (after);
144 if (res == 1 && EMACS_SECS (after) == EMACS_SECS (time_before))
145 {
146 boot_time = EMACS_SECS (time_before) - (time_t) upsecs;
147 return boot_time;
148 }
149
150 /* Otherwise, try again to read the uptime. */
151 time_before = after;
152 }
f805a125
KH
153#if defined (CTL_KERN) && defined (KERN_BOOTTIME)
154 {
155 int mib[2];
156 size_t size;
157 struct timeval boottime_val;
158
159 mib[0] = CTL_KERN;
160 mib[1] = KERN_BOOTTIME;
161 size = sizeof (boottime_val);
162
163 if (sysctl (mib, 2, &boottime_val, &size, NULL, 0) >= 0)
164 {
165 boot_time = boottime_val.tv_sec;
166 return boot_time;
167 }
168 }
169#endif /* defined (CTL_KERN) && defined (KERN_BOOTTIME) */
9177d978 170
c6fd1dc4 171#if defined (BOOT_TIME) && ! defined (NO_WTMP_FILE)
b97771fc
RS
172#ifndef CANNOT_DUMP
173 /* The utmp routines maintain static state.
174 Don't touch that state unless we are initialized,
175 since it might not survive dumping. */
176 if (! initialized)
177 return boot_time;
178#endif /* not CANNOT_DUMP */
179
180 /* Try to get boot time from utmp before wtmp,
181 since utmp is typically much smaller than wtmp.
182 Passing a null pointer causes get_boot_time_1
183 to inspect the default file, namely utmp. */
184 get_boot_time_1 ((char *) 0, 0);
185 if (boot_time)
186 return boot_time;
187
9177d978 188 /* Try to get boot time from the current wtmp file. */
b97771fc 189 get_boot_time_1 (WTMP_FILE, 1);
9177d978
RS
190
191 /* If we did not find a boot time in wtmp, look at wtmp, and so on. */
b97771fc 192 for (counter = 0; counter < 20 && ! boot_time; counter++)
9177d978
RS
193 {
194 char cmd_string[100];
195 Lisp_Object tempname, filename;
196 int delete_flag = 0;
197
198 filename = Qnil;
199
77e544a4 200 sprintf (cmd_string, "%s.%d", WTMP_FILE, counter);
9177d978 201 tempname = build_string (cmd_string);
29a2adb0 202 if (! NILP (Ffile_exists_p (tempname)))
9177d978
RS
203 filename = tempname;
204 else
205 {
77e544a4 206 sprintf (cmd_string, "%s.%d.gz", WTMP_FILE, counter);
9177d978
RS
207 tempname = build_string (cmd_string);
208 if (! NILP (Ffile_exists_p (tempname)))
209 {
210 Lisp_Object args[6];
211 tempname = Fmake_temp_name (build_string ("wtmp"));
212 args[0] = Vshell_file_name;
213 args[1] = Qnil;
214 args[2] = Qnil;
215 args[3] = Qnil;
216 args[4] = build_string ("-c");
77e544a4
RS
217 sprintf (cmd_string, "gunzip < %s.%d.gz > %s",
218 WTMP_FILE, counter, XSTRING (tempname)->data);
9177d978
RS
219 args[5] = build_string (cmd_string);
220 Fcall_process (6, args);
221 filename = tempname;
222 delete_flag = 1;
223 }
224 }
225
226 if (! NILP (filename))
227 {
b97771fc 228 get_boot_time_1 (XSTRING (filename)->data, 1);
9177d978
RS
229 if (delete_flag)
230 unlink (XSTRING (filename)->data);
231 }
232 }
233
234 return boot_time;
235#else
236 return 0;
237#endif
238}
239
e9f22ced 240#ifdef BOOT_TIME
9177d978
RS
241/* Try to get the boot time from wtmp file FILENAME.
242 This succeeds if that file contains a reboot record.
9177d978 243
b97771fc
RS
244 If FILENAME is zero, use the same file as before;
245 if no FILENAME has ever been specified, this is the utmp file.
246 Use the newest reboot record if NEWEST is nonzero,
247 the first reboot record otherwise.
248 Ignore all reboot records on or before BOOT_TIME.
249 Success is indicated by setting BOOT_TIME to a larger value. */
250
251get_boot_time_1 (filename, newest)
9177d978 252 char *filename;
b97771fc 253 int newest;
9177d978
RS
254{
255 struct utmp ut, *utp;
77e544a4
RS
256 int desc;
257
b97771fc
RS
258 if (filename)
259 {
260 /* On some versions of IRIX, opening a nonexistent file name
261 is likely to crash in the utmp routines. */
262 desc = open (filename, O_RDONLY);
263 if (desc < 0)
264 return;
265
266 close (desc);
267
268 utmpname (filename);
269 }
9177d978 270
c321b190 271 setutent ();
b97771fc 272
c321b190
RS
273 while (1)
274 {
275 /* Find the next reboot record. */
276 ut.ut_type = BOOT_TIME;
277 utp = getutid (&ut);
278 if (! utp)
279 break;
280 /* Compare reboot times and use the newest one. */
281 if (utp->ut_time > boot_time)
b97771fc
RS
282 {
283 boot_time = utp->ut_time;
284 if (! newest)
285 break;
286 }
c321b190
RS
287 /* Advance on element in the file
288 so that getutid won't repeat the same one. */
289 utp = getutent ();
290 if (! utp)
291 break;
292 }
15e88d21 293 endutent ();
15e88d21 294}
e9f22ced 295#endif /* BOOT_TIME */
15e88d21 296\f
8dbbc384 297/* Here is the structure that stores information about a lock. */
32676c08 298
8dbbc384
RS
299typedef struct
300{
301 char *user;
302 char *host;
9005cb4f 303 unsigned long pid;
15e88d21 304 time_t boot_time;
8dbbc384 305} lock_info_type;
32676c08 306
49b6d120
RS
307/* When we read the info back, we might need this much more,
308 enough for decimal representation plus null. */
309#define LOCK_PID_MAX (4 * sizeof (unsigned long))
32676c08 310
8dbbc384
RS
311/* Free the two dynamically-allocated pieces in PTR. */
312#define FREE_LOCK_INFO(i) do { xfree ((i).user); xfree ((i).host); } while (0)
e31fbc7a 313
e31fbc7a 314
8dbbc384
RS
315/* Write the name of the lock file for FN into LFNAME. Length will be
316 that of FN plus two more for the leading `.#' plus one for the null. */
7b92975f 317#define MAKE_LOCK_NAME(lock, file) \
fc932ac6 318 (lock = (char *) alloca (STRING_BYTES (XSTRING (file)) + 2 + 1), \
8dbbc384 319 fill_in_lock_file_name (lock, (file)))
e31fbc7a 320
8dbbc384
RS
321static void
322fill_in_lock_file_name (lockfile, fn)
e31fbc7a
RS
323 register char *lockfile;
324 register Lisp_Object fn;
325{
8dbbc384
RS
326 register char *p;
327
328 strcpy (lockfile, XSTRING (fn)->data);
329
330 /* Shift the nondirectory part of the file name (including the null)
331 right two characters. Here is one of the places where we'd have to
332 do something to support 14-character-max file names. */
333 for (p = lockfile + strlen (lockfile); p != lockfile && *p != '/'; p--)
334 p[2] = *p;
e31fbc7a 335
8dbbc384
RS
336 /* Insert the `.#'. */
337 p[1] = '.';
338 p[2] = '#';
339}
e31fbc7a 340
8dbbc384
RS
341/* Lock the lock file named LFNAME.
342 If FORCE is nonzero, we do so even if it is already locked.
343 Return 1 if successful, 0 if not. */
e31fbc7a 344
8dbbc384
RS
345static int
346lock_file_1 (lfname, force)
347 char *lfname;
348 int force;
349{
350 register int err;
bd26d5a3 351 time_t boot_time;
662c2ef2
RS
352 char *user_name;
353 char *host_name;
354 char *lock_info_str;
355
356 if (STRINGP (Fuser_login_name (Qnil)))
266d7a00 357 user_name = (char *)XSTRING (Fuser_login_name (Qnil))->data;
662c2ef2
RS
358 else
359 user_name = "";
360 if (STRINGP (Fsystem_name ()))
266d7a00 361 host_name = (char *)XSTRING (Fsystem_name ())->data;
662c2ef2
RS
362 else
363 host_name = "";
266d7a00 364 lock_info_str = (char *)alloca (strlen (user_name) + strlen (host_name)
15e88d21 365 + LOCK_PID_MAX + 5);
8dbbc384 366
bd26d5a3
RS
367 boot_time = get_boot_time ();
368 if (boot_time)
369 sprintf (lock_info_str, "%s@%s.%lu:%lu", user_name, host_name,
370 (unsigned long) getpid (), (unsigned long) boot_time);
371 else
372 sprintf (lock_info_str, "%s@%s.%lu", user_name, host_name,
373 (unsigned long) getpid ());
8dbbc384
RS
374
375 err = symlink (lock_info_str, lfname);
376 if (errno == EEXIST && force)
e31fbc7a 377 {
8dbbc384
RS
378 unlink (lfname);
379 err = symlink (lock_info_str, lfname);
e31fbc7a 380 }
e31fbc7a 381
8dbbc384
RS
382 return err == 0;
383}
e31fbc7a 384
9177d978 385/* Return 1 if times A and B are no more than one second apart. */
32676c08 386
9177d978
RS
387int
388within_one_second (a, b)
389 time_t a, b;
390{
391 return (a - b >= -1 && a - b <= 1);
392}
8dbbc384
RS
393\f
394/* Return 0 if nobody owns the lock file LFNAME or the lock is obsolete,
395 1 if another process owns it (and set OWNER (if non-null) to info),
396 2 if the current process owns it,
397 or -1 if something is wrong with the locking mechanism. */
e31fbc7a 398
8dbbc384
RS
399static int
400current_lock_owner (owner, lfname)
401 lock_info_type *owner;
402 char *lfname;
32676c08 403{
8dbbc384
RS
404#ifndef index
405 extern char *rindex (), *index ();
406#endif
407 int o, p, len, ret;
408 int local_owner = 0;
15e88d21 409 char *at, *dot, *colon;
8dbbc384
RS
410 char *lfinfo = 0;
411 int bufsize = 50;
412 /* Read arbitrarily-long contents of symlink. Similar code in
413 file-symlink-p in fileio.c. */
414 do
415 {
416 bufsize *= 2;
417 lfinfo = (char *) xrealloc (lfinfo, bufsize);
418 len = readlink (lfname, lfinfo, bufsize);
419 }
420 while (len >= bufsize);
421
422 /* If nonexistent lock file, all is well; otherwise, got strange error. */
423 if (len == -1)
424 {
425 xfree (lfinfo);
426 return errno == ENOENT ? 0 : -1;
427 }
32676c08 428
8dbbc384
RS
429 /* Link info exists, so `len' is its length. Null terminate. */
430 lfinfo[len] = 0;
431
432 /* Even if the caller doesn't want the owner info, we still have to
433 read it to determine return value, so allocate it. */
434 if (!owner)
435 {
3609a53b 436 owner = (lock_info_type *) alloca (sizeof (lock_info_type));
8dbbc384
RS
437 local_owner = 1;
438 }
439
15e88d21 440 /* Parse USER@HOST.PID:BOOT_TIME. If can't parse, return -1. */
8dbbc384
RS
441 /* The USER is everything before the first @. */
442 at = index (lfinfo, '@');
443 dot = rindex (lfinfo, '.');
15e88d21
RS
444 if (!at || !dot)
445 {
446 xfree (lfinfo);
447 return -1;
448 }
8dbbc384
RS
449 len = at - lfinfo;
450 owner->user = (char *) xmalloc (len + 1);
451 strncpy (owner->user, lfinfo, len);
452 owner->user[len] = 0;
453
15e88d21 454 /* The PID is everything from the last `.' to the `:'. */
8dbbc384 455 owner->pid = atoi (dot + 1);
15e88d21
RS
456 colon = dot;
457 while (*colon && *colon != ':')
458 colon++;
459 /* After the `:', if there is one, comes the boot time. */
460 if (*colon == ':')
461 owner->boot_time = atoi (colon + 1);
462 else
463 owner->boot_time = 0;
32676c08 464
8dbbc384
RS
465 /* The host is everything in between. */
466 len = dot - at - 1;
467 owner->host = (char *) xmalloc (len + 1);
468 strncpy (owner->host, at + 1, len);
469 owner->host[len] = 0;
32676c08 470
8dbbc384
RS
471 /* We're done looking at the link info. */
472 xfree (lfinfo);
473
474 /* On current host? */
662c2ef2
RS
475 if (STRINGP (Fsystem_name ())
476 && strcmp (owner->host, XSTRING (Fsystem_name ())->data) == 0)
32676c08 477 {
8dbbc384
RS
478 if (owner->pid == getpid ())
479 ret = 2; /* We own it. */
72dcef0e 480 else if (owner->pid > 0
15e88d21
RS
481 && (kill (owner->pid, 0) >= 0 || errno == EPERM)
482 && (owner->boot_time == 0
9177d978 483 || within_one_second (owner->boot_time, get_boot_time ())))
8dbbc384 484 ret = 1; /* An existing process on this machine owns it. */
8dbbc384
RS
485 /* The owner process is dead or has a strange pid (<=0), so try to
486 zap the lockfile. */
72dcef0e 487 else if (unlink (lfname) < 0)
8dbbc384 488 ret = -1;
72dcef0e
RS
489 else
490 ret = 0;
32676c08 491 }
8dbbc384
RS
492 else
493 { /* If we wanted to support the check for stale locks on remote machines,
494 here's where we'd do it. */
495 ret = 1;
496 }
497
498 /* Avoid garbage. */
499 if (local_owner || ret <= 0)
500 {
501 FREE_LOCK_INFO (*owner);
502 }
503 return ret;
32676c08
JB
504}
505
8dbbc384
RS
506\f
507/* Lock the lock named LFNAME if possible.
508 Return 0 in that case.
509 Return positive if some other process owns the lock, and info about
510 that process in CLASHER.
511 Return -1 if cannot lock for any other reason. */
8489eb67 512
8dbbc384
RS
513static int
514lock_if_free (clasher, lfname)
515 lock_info_type *clasher;
516 register char *lfname;
517{
c6c0c4b1 518 if (lock_file_1 (lfname, 0) == 0)
8dbbc384
RS
519 {
520 int locker;
e0e0205b 521
8dbbc384
RS
522 if (errno != EEXIST)
523 return -1;
524
525 locker = current_lock_owner (clasher, lfname);
526 if (locker == 2)
527 {
528 FREE_LOCK_INFO (*clasher);
529 return 0; /* We ourselves locked it. */
530 }
531 else if (locker == 1)
532 return 1; /* Someone else has it. */
8dbbc384 533
c6c0c4b1 534 return -1; /* Something's wrong. */
8dbbc384
RS
535 }
536 return 0;
8489eb67
RS
537}
538
8dbbc384 539/* lock_file locks file FN,
8489eb67
RS
540 meaning it serves notice on the world that you intend to edit that file.
541 This should be done only when about to modify a file-visiting
542 buffer previously unmodified.
8dbbc384 543 Do not (normally) call this for a buffer already modified,
8489eb67
RS
544 as either the file is already locked, or the user has already
545 decided to go ahead without locking.
546
8dbbc384 547 When this returns, either the lock is locked for us,
8489eb67
RS
548 or the user has said to go ahead without locking.
549
8dbbc384 550 If the file is locked by someone else, this calls
8489eb67 551 ask-user-about-lock (a Lisp function) with two arguments,
8dbbc384 552 the file name and info about the user who did the locking.
8489eb67
RS
553 This function can signal an error, or return t meaning
554 take away the lock, or return nil meaning ignore the lock. */
555
8489eb67
RS
556void
557lock_file (fn)
eb4b1c05 558 Lisp_Object fn;
8489eb67 559{
f4a4528d 560 register Lisp_Object attack, orig_fn, encoded_fn;
8dbbc384
RS
561 register char *lfname, *locker;
562 lock_info_type lock_info;
8489eb67 563
5383bc6d 564 orig_fn = fn;
1e89de84 565 fn = Fexpand_file_name (fn, Qnil);
f4a4528d 566 encoded_fn = ENCODE_FILE (fn);
1e89de84 567
8dbbc384 568 /* Create the name of the lock-file for file fn */
f4a4528d 569 MAKE_LOCK_NAME (lfname, encoded_fn);
8489eb67 570
32676c08
JB
571 /* See if this file is visited and has changed on disk since it was
572 visited. */
8489eb67 573 {
a57bc488 574 register Lisp_Object subject_buf;
eb4b1c05 575 struct gcpro gcpro1;
3036594f 576
5383bc6d 577 subject_buf = get_truename_buffer (orig_fn);
3036594f
RS
578 GCPRO1 (fn);
579
265a9e55
JB
580 if (!NILP (subject_buf)
581 && NILP (Fverify_visited_file_modtime (subject_buf))
582 && !NILP (Ffile_exists_p (fn)))
8489eb67 583 call1 (intern ("ask-user-about-supersession-threat"), fn);
3036594f
RS
584
585 UNGCPRO;
8489eb67
RS
586 }
587
588 /* Try to lock the lock. */
8dbbc384
RS
589 if (lock_if_free (&lock_info, lfname) <= 0)
590 /* Return now if we have locked it, or if lock creation failed */
8489eb67
RS
591 return;
592
593 /* Else consider breaking the lock */
266d7a00
RS
594 locker = (char *) alloca (strlen (lock_info.user) + strlen (lock_info.host)
595 + LOCK_PID_MAX + 9);
79e51eeb 596 sprintf (locker, "%s@%s (pid %lu)", lock_info.user, lock_info.host,
8dbbc384
RS
597 lock_info.pid);
598 FREE_LOCK_INFO (lock_info);
599
600 attack = call2 (intern ("ask-user-about-lock"), fn, build_string (locker));
265a9e55 601 if (!NILP (attack))
8489eb67
RS
602 /* User says take the lock */
603 {
8dbbc384 604 lock_file_1 (lfname, 1);
8489eb67
RS
605 return;
606 }
607 /* User says ignore the lock */
608}
609
8489eb67
RS
610void
611unlock_file (fn)
612 register Lisp_Object fn;
613{
614 register char *lfname;
615
1e89de84 616 fn = Fexpand_file_name (fn, Qnil);
88eace34 617 fn = ENCODE_FILE (fn);
1e89de84 618
7b92975f 619 MAKE_LOCK_NAME (lfname, fn);
8489eb67 620
8dbbc384 621 if (current_lock_owner (0, lfname) == 2)
8489eb67 622 unlink (lfname);
8489eb67
RS
623}
624
625void
626unlock_all_files ()
627{
628 register Lisp_Object tail;
629 register struct buffer *b;
630
4e6c9d9e 631 for (tail = Vbuffer_alist; GC_CONSP (tail); tail = XCONS (tail)->cdr)
8489eb67
RS
632 {
633 b = XBUFFER (XCONS (XCONS (tail)->car)->cdr);
5757b805 634 if (STRINGP (b->file_truename) && BUF_SAVE_MODIFF (b) < BUF_MODIFF (b))
1c343051
KH
635 {
636 register char *lfname;
637
638 MAKE_LOCK_NAME (lfname, b->file_truename);
639
640 if (current_lock_owner (0, lfname) == 2)
641 unlink (lfname);
642 }
8489eb67
RS
643 }
644}
8489eb67
RS
645\f
646DEFUN ("lock-buffer", Flock_buffer, Slock_buffer,
647 0, 1, 0,
648 "Lock FILE, if current buffer is modified.\n\
649FILE defaults to current buffer's visited file,\n\
650or else nothing is done if current buffer isn't visiting a file.")
e9319ef2
EN
651 (file)
652 Lisp_Object file;
8489eb67 653{
e9319ef2
EN
654 if (NILP (file))
655 file = current_buffer->file_truename;
8489eb67 656 else
e9319ef2 657 CHECK_STRING (file, 0);
6a140159 658 if (SAVE_MODIFF < MODIFF
e9319ef2
EN
659 && !NILP (file))
660 lock_file (file);
8489eb67
RS
661 return Qnil;
662}
663
664DEFUN ("unlock-buffer", Funlock_buffer, Sunlock_buffer,
665 0, 0, 0,
666 "Unlock the file visited in the current buffer,\n\
667if it should normally be locked.")
668 ()
669{
6a140159 670 if (SAVE_MODIFF < MODIFF
5757b805
RS
671 && STRINGP (current_buffer->file_truename))
672 unlock_file (current_buffer->file_truename);
8489eb67
RS
673 return Qnil;
674}
675
8489eb67
RS
676/* Unlock the file visited in buffer BUFFER. */
677
d07e0802 678void
8489eb67
RS
679unlock_buffer (buffer)
680 struct buffer *buffer;
681{
6a140159 682 if (BUF_SAVE_MODIFF (buffer) < BUF_MODIFF (buffer)
5757b805
RS
683 && STRINGP (buffer->file_truename))
684 unlock_file (buffer->file_truename);
8489eb67
RS
685}
686
687DEFUN ("file-locked-p", Ffile_locked_p, Sfile_locked_p, 0, 1, 0,
688 "Return nil if the FILENAME is not locked,\n\
689t if it is locked by you, else a string of the name of the locker.")
e9319ef2
EN
690 (filename)
691 Lisp_Object filename;
8489eb67 692{
8dbbc384 693 Lisp_Object ret;
8489eb67
RS
694 register char *lfname;
695 int owner;
8dbbc384 696 lock_info_type locker;
8489eb67 697
e9319ef2 698 filename = Fexpand_file_name (filename, Qnil);
8489eb67 699
e9319ef2 700 MAKE_LOCK_NAME (lfname, filename);
8489eb67 701
8dbbc384 702 owner = current_lock_owner (&locker, lfname);
8489eb67 703 if (owner <= 0)
8dbbc384
RS
704 ret = Qnil;
705 else if (owner == 2)
706 ret = Qt;
707 else
708 ret = build_string (locker.user);
709
710 if (owner > 0)
711 FREE_LOCK_INFO (locker);
712
713 return ret;
8489eb67 714}
32676c08
JB
715\f
716/* Initialization functions. */
717
a3fd58aa
KH
718void
719init_filelock ()
720{
721 boot_time = 0;
b97771fc 722 boot_time_initialized = 0;
a3fd58aa
KH
723}
724
dfcf069d 725void
8489eb67
RS
726syms_of_filelock ()
727{
728 defsubr (&Sunlock_buffer);
729 defsubr (&Slock_buffer);
730 defsubr (&Sfile_locked_p);
731}
732
733#endif /* CLASH_DETECTION */