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