/* File IO for GNU Emacs.
- Copyright (C) 1985, 1986, 1987, 1988, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1985, 1986, 1987, 1988, 1993 Free Software Foundation, Inc.
This file is part of GNU Emacs.
along with GNU Emacs; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-#include "config.h"
+#include <config.h>
#include <sys/types.h>
#include <sys/stat.h>
+#if !defined (S_ISLNK) && defined (S_IFLNK)
+# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+
+#if !defined (S_ISREG) && defined (S_IFREG)
+# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+
#ifdef VMS
#include "vms-pwd.h"
#else
#include <sys/time.h>
#endif
+#ifndef USG
+#ifndef VMS
+#ifndef BSD4_1
+#define HAVE_FSYNC
+#endif
+#endif
+#endif
+
#include "lisp.h"
#include "intervals.h"
#include "buffer.h"
#ifdef HPUX
#include <netio.h>
#ifndef HPUX8
+#ifndef HPUX9
#include <errnet.h>
#endif
#endif
+#endif
#ifndef O_WRONLY
#define O_WRONLY 1
whose I/O is done with a special handler. */
Lisp_Object Vfile_name_handler_alist;
+/* Functions to be called to process text properties in inserted file. */
+Lisp_Object Vafter_insert_file_functions;
+
+/* Functions to be called to create text property annotations for file. */
+Lisp_Object Vwrite_region_annotate_functions;
+
/* Nonzero means, when reading a filename in the minibuffer,
start out by inserting the default directory into the minibuffer. */
int insert_default_directory;
Lisp_Object Qfile_name_history;
+Lisp_Object Qcar_less_than_car;
+
report_file_error (string, data)
char *string;
Lisp_Object data;
Lisp_Object Qinsert_file_contents;
Lisp_Object Qwrite_region;
Lisp_Object Qverify_visited_file_modtime;
+Lisp_Object Qset_visited_file_modtime;
DEFUN ("find-file-name-handler", Ffind_file_name_handler, Sfind_file_name_handler, 1, 1, 0,
"Return FILENAME's handler function, if its syntax is handled specially.\n\
Lisp_Object filename;
{
/* This function must not munge the match data. */
-
Lisp_Object chain;
+
+ CHECK_STRING (filename, 0);
+
for (chain = Vfile_name_handler_alist; XTYPE (chain) == Lisp_Cons;
chain = XCONS (chain)->cdr)
{
int tlen;
unsigned char *target;
struct passwd *pw;
- int lose;
#ifdef VMS
unsigned char * colon = 0;
unsigned char * close = 0;
if (!NILP (handler))
return call3 (handler, Qexpand_file_name, name, defalt);
+ /* Use the buffer's default-directory if DEFALT is omitted. */
+ if (NILP (defalt))
+ defalt = current_buffer->directory;
+ CHECK_STRING (defalt, 1);
+
+ /* Make sure DEFALT is properly expanded.
+ It would be better to do this down below where we actually use
+ defalt. Unfortunately, calling Fexpand_file_name recursively
+ could invoke GC, and the strings might be relocated. This would
+ be annoying because we have pointers into strings lying around
+ that would need adjusting, and people would add new pointers to
+ the code and forget to adjust them, resulting in intermittent bugs.
+ Putting this call here avoids all that crud.
+
+ The EQ test avoids infinite recursion. */
+ if (! NILP (defalt) && !EQ (defalt, name)
+ /* This saves time in a common case. */
+ && XSTRING (defalt)->data[0] != '/')
+ {
+ struct gcpro gcpro1;
+
+ GCPRO1 (name);
+ defalt = Fexpand_file_name (defalt, Qnil);
+ UNGCPRO;
+ }
+
#ifdef VMS
/* Filenames on VMS are always upper case. */
name = Fupcase (name);
#endif /* VMS */
)
{
+ /* If it turns out that the filename we want to return is just a
+ suffix of FILENAME, we don't need to go through and edit
+ things; we just need to construct a new string using data
+ starting at the middle of FILENAME. If we set lose to a
+ non-zero value, that means we've discovered that we can't do
+ that cool trick. */
+ int lose = 0;
+
p = nm;
- lose = 0;
while (*p)
{
/* Since we know the path is absolute, we can assume that each
#endif /* not VMS */
&& !newdir)
{
- if (NILP (defalt))
- defalt = current_buffer->directory;
- CHECK_STRING (defalt, 1);
newdir = XSTRING (defalt)->data;
}
{
/* Get rid of any slash at the end of newdir. */
int length = strlen (newdir);
+ /* Adding `length > 1 &&' makes ~ expand into / when homedir
+ is the root dir. People disagree about whether that is right.
+ Anyway, we can't take the risk of this change now. */
if (newdir[length - 1] == '/')
{
unsigned char *temp = (unsigned char *) alloca (length);
}
\f
/* A slightly faster and more convenient way to get
- (directory-file-name (expand-file-name FOO)). The return value may
- have had its last character zapped with a '\0' character, meaning
- that it is acceptable to system calls, but not to other lisp
- functions. Callers should make sure that the return value doesn't
- escape. */
+ (directory-file-name (expand-file-name FOO)). */
Lisp_Object
expand_and_dir_to_file (filename, defdir)
stat behaves differently depending! */
if (XSTRING (abspath)->size > 1
&& XSTRING (abspath)->data[XSTRING (abspath)->size - 1] == '/')
- {
- if (EQ (abspath, filename))
- abspath = Fcopy_sequence (abspath);
- XSTRING (abspath)->data[XSTRING (abspath)->size - 1] = 0;
- }
+ /* We cannot take shortcuts; they might be wrong for magic file names. */
+ abspath = Fdirectory_file_name (abspath);
#endif
return abspath;
}
Lisp_Object handler;
struct gcpro gcpro1, gcpro2;
int count = specpdl_ptr - specpdl;
+ Lisp_Object args[6];
+ int input_file_statable_p;
GCPRO2 (filename, newname);
CHECK_STRING (filename, 0);
/* If the input file name has special constructs in it,
call the corresponding file handler. */
handler = Ffind_file_name_handler (filename);
- if (!NILP (handler))
- return call3 (handler, Qcopy_file, filename, newname);
/* Likewise for output file name. */
- handler = Ffind_file_name_handler (newname);
+ if (NILP (handler))
+ handler = Ffind_file_name_handler (newname);
if (!NILP (handler))
- return call3 (handler, Qcopy_file, filename, newname);
+ return call5 (handler, Qcopy_file, filename, newname,
+ ok_if_already_exists, keep_date);
if (NILP (ok_if_already_exists)
|| XTYPE (ok_if_already_exists) == Lisp_Int)
record_unwind_protect (close_file_unwind, make_number (ifd));
+ /* We can only copy regular files and symbolic links. Other files are not
+ copyable by us. */
+ input_file_statable_p = (fstat (ifd, &st) >= 0);
+
+#if defined (S_ISREG) && defined (S_ISLNK)
+ if (input_file_statable_p)
+ {
+ if (!(S_ISREG (st.st_mode)) && !(S_ISLNK (st.st_mode)))
+ {
+#if defined (EISDIR)
+ /* Get a better looking error message. */
+ errno = EISDIR;
+#endif /* EISDIR */
+ report_file_error ("Non-regular file", Fcons (filename, Qnil));
+ }
+ }
+#endif /* S_ISREG && S_ISLNK */
+
#ifdef VMS
/* Create the copy file with the same record format as the input file */
ofd = sys_creat (XSTRING (newname)->data, 0666, ifd);
report_file_error ("I/O error", Fcons (newname, Qnil));
immediate_quit = 0;
- if (fstat (ifd, &st) >= 0)
+ if (input_file_statable_p)
{
if (!NILP (keep_date))
{
/* If the file name has special constructs in it,
call the corresponding file handler. */
handler = Ffind_file_name_handler (filename);
+ if (NILP (handler))
+ handler = Ffind_file_name_handler (newname);
if (!NILP (handler))
- return call3 (handler, Qrename_file, filename, newname);
+ return call4 (handler, Qrename_file,
+ filename, newname, ok_if_already_exists);
if (NILP (ok_if_already_exists)
|| XTYPE (ok_if_already_exists) == Lisp_Int)
{
if (errno == EXDEV)
{
- Fcopy_file (filename, newname, ok_if_already_exists, Qt);
+ Fcopy_file (filename, newname,
+ /* We have already prompted if it was an integer,
+ so don't have copy-file prompt again. */
+ NILP (ok_if_already_exists) ? Qnil : Qt, Qt);
Fdelete_file (filename);
}
else
call the corresponding file handler. */
handler = Ffind_file_name_handler (filename);
if (!NILP (handler))
- return call3 (handler, Qadd_name_to_file, filename, newname);
+ return call4 (handler, Qadd_name_to_file, filename, newname,
+ ok_if_already_exists);
if (NILP (ok_if_already_exists)
|| XTYPE (ok_if_already_exists) == Lisp_Int)
call the corresponding file handler. */
handler = Ffind_file_name_handler (filename);
if (!NILP (handler))
- return call3 (handler, Qmake_symbolic_link, filename, linkname);
+ return call4 (handler, Qmake_symbolic_link, filename, linkname,
+ ok_if_already_exists);
if (NILP (ok_if_already_exists)
|| XTYPE (ok_if_already_exists) == Lisp_Int)
/* If we didn't complain already, silently delete existing file. */
if (errno == EEXIST)
{
- unlink (XSTRING (filename)->data);
+ unlink (XSTRING (linkname)->data);
if (0 <= symlink (XSTRING (filename)->data, XSTRING (linkname)->data))
return Qnil;
}
DEFUN ("file-executable-p", Ffile_executable_p, Sfile_executable_p, 1, 1, 0,
"Return t if FILENAME can be executed by you.\n\
-For directories this means you can change to that directory.")
+For a directory, this means you can access files in that directory.")
(filename)
Lisp_Object filename;
valsize = readlink (XSTRING (filename)->data, buf, bufsize);
if (valsize < bufsize) break;
/* Buffer was not long enough */
- free (buf);
+ xfree (buf);
bufsize *= 2;
}
if (valsize == -1)
{
- free (buf);
+ xfree (buf);
return Qnil;
}
val = make_string (buf, valsize);
- free (buf);
+ xfree (buf);
return val;
#else /* not S_IFLNK */
return Qnil;
#endif /* not S_IFLNK */
}
+#ifdef SOLARIS_BROKEN_ACCESS
+/* In Solaris 2.1, the readonly-ness of the filesystem is not
+ considered by the access system call. This is Sun's bug, but we
+ still have to make Emacs work. */
+
+#include <sys/statvfs.h>
+
+static int
+ro_fsys (path)
+ char *path;
+{
+ struct statvfs statvfsb;
+
+ if (statvfs(path, &statvfsb))
+ return 1; /* error from statvfs, be conservative and say not wrtable */
+ else
+ /* Otherwise, fsys is ro if bit is set. */
+ return statvfsb.f_flag & ST_RDONLY;
+}
+#else
+/* But on every other os, access has already done the right thing. */
+#define ro_fsys(path) 0
+#endif
+
/* Having this before file-symlink-p mysteriously caused it to be forgotten
on the RT/PC. */
DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
return call2 (handler, Qfile_writable_p, abspath);
if (access (XSTRING (abspath)->data, 0) >= 0)
- return (access (XSTRING (abspath)->data, 2) >= 0) ? Qt : Qnil;
+ return ((access (XSTRING (abspath)->data, 2) >= 0
+ && ! ro_fsys ((char *) XSTRING (abspath)->data))
+ ? Qt : Qnil);
dir = Ffile_name_directory (abspath);
#ifdef VMS
if (!NILP (dir))
dir = Fdirectory_file_name (dir);
#endif /* VMS */
- return (access (!NILP (dir) ? (char *) XSTRING (dir)->data : "", 2) >= 0
+ return ((access (!NILP (dir) ? (char *) XSTRING (dir)->data : "", 2) >= 0
+ && ! ro_fsys ((char *) XSTRING (dir)->data))
? Qt : Qnil);
}
/* If the file name has special constructs in it,
call the corresponding file handler. */
handler = Ffind_file_name_handler (abspath1);
+ if (NILP (handler))
+ handler = Ffind_file_name_handler (abspath2);
if (!NILP (handler))
return call3 (handler, Qfile_newer_than_file_p, abspath1, abspath2);
}
\f
DEFUN ("insert-file-contents", Finsert_file_contents, Sinsert_file_contents,
- 1, 2, 0,
+ 1, 4, 0,
"Insert contents of file FILENAME after point.\n\
-Returns list of absolute pathname and length of data inserted.\n\
+Returns list of absolute file name and length of data inserted.\n\
If second argument VISIT is non-nil, the buffer's visited filename\n\
and last save file modtime are set, and it is marked unmodified.\n\
If visiting and the file does not exist, visiting is completed\n\
-before the error is signaled.")
- (filename, visit)
- Lisp_Object filename, visit;
+before the error is signaled.\n\n\
+The optional third and fourth arguments BEG and END\n\
+specify what portion of the file to insert.\n\
+If VISIT is non-nil, BEG and END must be nil.")
+ (filename, visit, beg, end)
+ Lisp_Object filename, visit, beg, end;
{
struct stat st;
register int fd;
register int inserted = 0;
register int how_much;
int count = specpdl_ptr - specpdl;
- struct gcpro gcpro1;
- Lisp_Object handler, val;
+ struct gcpro gcpro1, gcpro2;
+ Lisp_Object handler, val, insval;
+ Lisp_Object p;
+ int total;
val = Qnil;
+ p = Qnil;
- GCPRO1 (filename);
+ GCPRO2 (filename, p);
if (!NILP (current_buffer->read_only))
Fbarf_if_buffer_read_only();
handler = Ffind_file_name_handler (filename);
if (!NILP (handler))
{
- val = call3 (handler, Qinsert_file_contents, filename, visit);
+ val = call5 (handler, Qinsert_file_contents, filename, visit, beg, end);
st.st_mtime = 0;
goto handled;
}
if (st.st_size < 0)
error ("File size is negative");
+ if (!NILP (beg) || !NILP (end))
+ if (!NILP (visit))
+ error ("Attempt to visit less than an entire file");
+
+ if (!NILP (beg))
+ CHECK_NUMBER (beg, 0);
+ else
+ XFASTINT (beg) = 0;
+
+ if (!NILP (end))
+ CHECK_NUMBER (end, 0);
+ else
+ {
+ XSETINT (end, st.st_size);
+ if (XINT (end) != st.st_size)
+ error ("maximum buffer size exceeded");
+ }
+
+ total = XINT (end) - XINT (beg);
+
{
register Lisp_Object temp;
/* Make sure point-max won't overflow after this insertion. */
- XSET (temp, Lisp_Int, st.st_size + Z);
- if (st.st_size + Z != XINT (temp))
+ XSET (temp, Lisp_Int, total);
+ if (total != XINT (temp))
error ("maximum buffer size exceeded");
}
- if (NILP (visit))
+ if (NILP (visit) && total > 0)
prepare_to_modify_buffer (point, point);
move_gap (point);
- if (GAP_SIZE < st.st_size)
- make_gap (st.st_size - GAP_SIZE);
-
+ if (GAP_SIZE < total)
+ make_gap (total - GAP_SIZE);
+
+ if (XINT (beg) != 0)
+ {
+ if (lseek (fd, XINT (beg), 0) < 0)
+ report_file_error ("Setting file position", Fcons (filename, Qnil));
+ }
+
while (1)
{
- int try = min (st.st_size - inserted, 64 << 10);
+ int try = min (total - inserted, 64 << 10);
int this;
/* Allow quitting out of the actual I/O. */
report_file_error ("Opening input file", Fcons (filename, Qnil));
}
- signal_after_change (point, 0, inserted);
+ if (NILP (visit) && total > 0)
+ signal_after_change (point, 0, inserted);
+ if (inserted > 0)
+ {
+ p = Vafter_insert_file_functions;
+ while (!NILP (p))
+ {
+ insval = call1 (Fcar (p), make_number (inserted));
+ if (!NILP (insval))
+ {
+ CHECK_NUMBER (insval, 0);
+ inserted = XFASTINT (insval);
+ }
+ QUIT;
+ p = Fcdr (p);
+ }
+ }
+
if (!NILP (val))
RETURN_UNGCPRO (val);
RETURN_UNGCPRO (Fcons (filename,
Fcons (make_number (inserted),
Qnil)));
}
+\f
+static Lisp_Object build_annotations ();
DEFUN ("write-region", Fwrite_region, Swrite_region, 3, 5,
"r\nFWrite region to file: ",
unsigned char *fname = 0; /* If non-0, original filename (must rename) */
#endif /* VMS */
Lisp_Object handler;
- Lisp_Object visit_file = XTYPE (visit) == Lisp_String ? visit : filename;
+ Lisp_Object visit_file;
+ Lisp_Object annotations;
+ int visiting, quietly;
struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
/* Special kludge to simplify auto-saving */
else if (XTYPE (start) != Lisp_String)
validate_region (&start, &end);
- GCPRO4 (start, filename, visit, visit_file);
filename = Fexpand_file_name (filename, Qnil);
+ if (XTYPE (visit) == Lisp_String)
+ visit_file = Fexpand_file_name (visit, Qnil);
+ else
+ visit_file = filename;
+
+ visiting = (EQ (visit, Qt) || XTYPE (visit) == Lisp_String);
+ quietly = !NILP (visit);
+
+ annotations = Qnil;
+
+ GCPRO4 (start, filename, annotations, visit_file);
/* If the file name has special constructs in it,
call the corresponding file handler. */
if (!NILP (handler))
{
- Lisp_Object args[7];
Lisp_Object val;
- args[0] = handler;
- args[1] = Qwrite_region;
- args[2] = start;
- args[3] = end;
- args[4] = filename;
- args[5] = append;
- args[6] = visit;
- val = Ffuncall (7, args);
+ val = call6 (handler, Qwrite_region, start, end,
+ filename, append, visit);
/* Do this before reporting IO error
to avoid a "file has changed on disk" warning on
next attempt to save. */
- if (EQ (visit, Qt) || XTYPE (visit) == Lisp_String)
+ if (visiting)
{
current_buffer->modtime = 0;
current_buffer->save_modified = MODIFF;
return val;
}
+ annotations = build_annotations (start, end);
+
#ifdef CLASH_DETECTION
if (!auto_saving)
lock_file (visit_file);
if (XTYPE (start) == Lisp_String)
{
- failure = 0 > e_write (desc, XSTRING (start)->data,
- XSTRING (start)->size);
+ failure = 0 > a_write (desc, XSTRING (start)->data,
+ XSTRING (start)->size, 0, &annotations);
save_errno = errno;
}
else if (XINT (start) != XINT (end))
{
+ int nwritten = 0;
if (XINT (start) < GPT)
{
register int end1 = XINT (end);
tem = XINT (start);
- failure = 0 > e_write (desc, &FETCH_CHAR (tem),
- min (GPT, end1) - tem);
+ failure = 0 > a_write (desc, &FETCH_CHAR (tem),
+ min (GPT, end1) - tem, tem, &annotations);
+ nwritten += min (GPT, end1) - tem;
save_errno = errno;
}
{
tem = XINT (start);
tem = max (tem, GPT);
- failure = 0 > e_write (desc, &FETCH_CHAR (tem), XINT (end) - tem);
+ failure = 0 > a_write (desc, &FETCH_CHAR (tem), XINT (end) - tem,
+ tem, &annotations);
+ nwritten += XINT (end) - tem;
+ save_errno = errno;
+ }
+
+ if (nwritten == 0)
+ {
+ /* If file was empty, still need to write the annotations */
+ failure = 0 > a_write (desc, "", 0, XINT (start), &annotations);
save_errno = errno;
}
}
immediate_quit = 0;
-#ifndef USG
-#ifndef VMS
-#ifndef BSD4_1
+#ifdef HAVE_FSYNC
/* Note fsync appears to change the modtime on BSD4.2 (both vax and sun).
Disk full in NFS may be reported here. */
- if (fsync (desc) < 0)
+ /* mib says that closing the file will try to write as fast as NFS can do
+ it, and that means the fsync here is not crucial for autosave files. */
+ if (!auto_saving && fsync (desc) < 0)
failure = 1, save_errno = errno;
-#endif
-#endif
#endif
/* Spurious "file has changed on disk" warnings have been
/* Do this before reporting IO error
to avoid a "file has changed on disk" warning on
next attempt to save. */
- if (EQ (visit, Qt) || XTYPE (visit) == Lisp_String)
+ if (visiting)
current_buffer->modtime = st.st_mtime;
if (failure)
error ("IO error writing %s: %s", fn, err_str (save_errno));
- if (EQ (visit, Qt) || XTYPE (visit) == Lisp_String)
+ if (visiting)
{
current_buffer->save_modified = MODIFF;
XFASTINT (current_buffer->save_length) = Z - BEG;
current_buffer->filename = visit_file;
}
- else if (!NILP (visit))
+ else if (quietly)
return Qnil;
if (!auto_saving)
return Qnil;
}
+Lisp_Object merge ();
+
+DEFUN ("car-less-than-car", Fcar_less_than_car, Scar_less_than_car, 2, 2, 0,
+ "Return t if (car A) is numerically less than (car B).")
+ (a, b)
+ Lisp_Object a, b;
+{
+ return Flss (Fcar (a), Fcar (b));
+}
+
+/* Build the complete list of annotations appropriate for writing out
+ the text between START and END, by calling all the functions in
+ write-region-annotate-functions and merging the lists they return. */
+
+static Lisp_Object
+build_annotations (start, end)
+ Lisp_Object start, end;
+{
+ Lisp_Object annotations;
+ Lisp_Object p, res;
+ struct gcpro gcpro1, gcpro2;
+
+ annotations = Qnil;
+ p = Vwrite_region_annotate_functions;
+ GCPRO2 (annotations, p);
+ while (!NILP (p))
+ {
+ res = call2 (Fcar (p), start, end);
+ Flength (res); /* Check basic validity of return value */
+ annotations = merge (annotations, res, Qcar_less_than_car);
+ p = Fcdr (p);
+ }
+ UNGCPRO;
+ return annotations;
+}
+
+/* Write to descriptor DESC the LEN characters starting at ADDR,
+ assuming they start at position POS in the buffer.
+ Intersperse with them the annotations from *ANNOT
+ (those which fall within the range of positions POS to POS + LEN),
+ each at its appropriate position.
+
+ Modify *ANNOT by discarding elements as we output them.
+ The return value is negative in case of system call failure. */
+
+int
+a_write (desc, addr, len, pos, annot)
+ int desc;
+ register char *addr;
+ register int len;
+ int pos;
+ Lisp_Object *annot;
+{
+ Lisp_Object tem;
+ int nextpos;
+ int lastpos = pos + len;
+
+ while (1)
+ {
+ tem = Fcar_safe (Fcar (*annot));
+ if (INTEGERP (tem) && XINT (tem) >= pos && XFASTINT (tem) <= lastpos)
+ nextpos = XFASTINT (tem);
+ else
+ return e_write (desc, addr, lastpos - pos);
+ if (nextpos > pos)
+ {
+ if (0 > e_write (desc, addr, nextpos - pos))
+ return -1;
+ addr += nextpos - pos;
+ pos = nextpos;
+ }
+ tem = Fcdr (Fcar (*annot));
+ if (STRINGP (tem))
+ {
+ if (0 > e_write (desc, XSTRING (tem)->data, XSTRING (tem)->size))
+ return -1;
+ }
+ *annot = Fcdr (*annot);
+ }
+}
+
int
e_write (desc, addr, len)
int desc;
return Qnil;
}
+DEFUN ("visited-file-modtime", Fvisited_file_modtime,
+ Svisited_file_modtime, 0, 0, 0,
+ "Return the current buffer's recorded visited file modification time.\n\
+The value is a list of the form (HIGH . LOW), like the time values\n\
+that `file-attributes' returns.")
+ ()
+{
+ return long_to_cons (current_buffer->modtime);
+}
+
DEFUN ("set-visited-file-modtime", Fset_visited_file_modtime,
- Sset_visited_file_modtime, 0, 0, 0,
+ Sset_visited_file_modtime, 0, 1, 0,
"Update buffer's recorded modification time from the visited file's time.\n\
Useful if the buffer was not read from the file normally\n\
-or if the file itself has been changed for some known benign reason.")
- ()
+or if the file itself has been changed for some known benign reason.\n\
+An argument specifies the modification time value to use\n\
+\(instead of that of the visited file), in the form of a list\n\
+\(HIGH . LOW) or (HIGH LOW).")
+ (time_list)
+ Lisp_Object time_list;
{
- register Lisp_Object filename;
- struct stat st;
- Lisp_Object handler;
-
- filename = Fexpand_file_name (current_buffer->filename, Qnil);
-
- /* If the file name has special constructs in it,
- call the corresponding file handler. */
- handler = Ffind_file_name_handler (filename);
- if (!NILP (handler))
- current_buffer->modtime = 0;
-
- else if (stat (XSTRING (filename)->data, &st) >= 0)
- current_buffer->modtime = st.st_mtime;
+ if (!NILP (time_list))
+ current_buffer->modtime = cons_to_long (time_list);
+ else
+ {
+ register Lisp_Object filename;
+ struct stat st;
+ Lisp_Object handler;
+
+ filename = Fexpand_file_name (current_buffer->filename, Qnil);
+
+ /* If the file name has special constructs in it,
+ call the corresponding file handler. */
+ handler = Ffind_file_name_handler (filename);
+ if (!NILP (handler))
+ /* The handler can find the file name the same way we did. */
+ return call2 (handler, Qset_visited_file_modtime, Qnil);
+ else if (stat (XSTRING (filename)->data, &st) >= 0)
+ current_buffer->modtime = st.st_mtime;
+ }
return Qnil;
}
This file is not the file you visited; that changes only when you save.\n\n\
Non-nil first argument means do not print any message if successful.\n\
Non-nil second argument means save only current buffer.")
- (nomsg)
- Lisp_Object nomsg;
+ (no_message, current_only)
+ Lisp_Object no_message, current_only;
{
struct buffer *old = current_buffer, *b;
Lisp_Object tail, buf;
int auto_saved = 0;
char *omessage = echo_area_glyphs;
- extern minibuf_level;
+ extern int minibuf_level;
+ int do_handled_files;
+ Lisp_Object oquit;
+
+ /* Ordinarily don't quit within this function,
+ but don't make it impossible to quit (in case we get hung in I/O). */
+ oquit = Vquit_flag;
+ Vquit_flag = Qnil;
/* No GCPRO needed, because (when it matters) all Lisp_Object variables
point to non-strings reached from Vbuffer_alist. */
auto_saving = 1;
if (minibuf_level)
- nomsg = Qt;
+ no_message = Qt;
/* Vrun_hooks is nil before emacs is dumped, and inc-vers.el will
eventually call do-auto-save, so don't err here in that case. */
if (!NILP (Vrun_hooks))
call1 (Vrun_hooks, intern ("auto-save-hook"));
- for (tail = Vbuffer_alist; XGCTYPE (tail) == Lisp_Cons;
- tail = XCONS (tail)->cdr)
- {
- buf = XCONS (XCONS (tail)->car)->cdr;
- b = XBUFFER (buf);
- /* Check for auto save enabled
- and file changed since last auto save
- and file changed since last real save. */
- if (XTYPE (b->auto_save_file_name) == Lisp_String
- && b->save_modified < BUF_MODIFF (b)
- && b->auto_save_modified < BUF_MODIFF (b))
- {
- if ((XFASTINT (b->save_length) * 10
- > (BUF_Z (b) - BUF_BEG (b)) * 13)
- /* A short file is likely to change a large fraction;
- spare the user annoying messages. */
- && XFASTINT (b->save_length) > 5000
- /* These messages are frequent and annoying for `*mail*'. */
- && !EQ (b->filename, Qnil))
- {
- /* It has shrunk too much; turn off auto-saving here. */
- message ("Buffer %s has shrunk a lot; auto save turned off there",
- XSTRING (b->name)->data);
- /* User can reenable saving with M-x auto-save. */
- b->auto_save_file_name = Qnil;
- /* Prevent warning from repeating if user does so. */
- XFASTINT (b->save_length) = 0;
- Fsleep_for (make_number (1), Qnil);
- continue;
- }
- set_buffer_internal (b);
- if (!auto_saved && NILP (nomsg))
- message1 ("Auto-saving...");
- internal_condition_case (auto_save_1, Qt, auto_save_error);
- auto_saved++;
- b->auto_save_modified = BUF_MODIFF (b);
- XFASTINT (current_buffer->save_length) = Z - BEG;
- set_buffer_internal (old);
- }
- }
+ /* First, save all files which don't have handlers. If Emacs is
+ crashing, the handlers may tweak what is causing Emacs to crash
+ in the first place, and it would be a shame if Emacs failed to
+ autosave perfectly ordinary files because it couldn't handle some
+ ange-ftp'd file. */
+ for (do_handled_files = 0; do_handled_files < 2; do_handled_files++)
+ for (tail = Vbuffer_alist; XGCTYPE (tail) == Lisp_Cons;
+ tail = XCONS (tail)->cdr)
+ {
+ buf = XCONS (XCONS (tail)->car)->cdr;
+ b = XBUFFER (buf);
+
+ if (!NILP (current_only)
+ && b != current_buffer)
+ continue;
+
+ /* Check for auto save enabled
+ and file changed since last auto save
+ and file changed since last real save. */
+ if (XTYPE (b->auto_save_file_name) == Lisp_String
+ && b->save_modified < BUF_MODIFF (b)
+ && b->auto_save_modified < BUF_MODIFF (b)
+ && (do_handled_files
+ || NILP (Ffind_file_name_handler (b->auto_save_file_name))))
+ {
+ if ((XFASTINT (b->save_length) * 10
+ > (BUF_Z (b) - BUF_BEG (b)) * 13)
+ /* A short file is likely to change a large fraction;
+ spare the user annoying messages. */
+ && XFASTINT (b->save_length) > 5000
+ /* These messages are frequent and annoying for `*mail*'. */
+ && !EQ (b->filename, Qnil)
+ && NILP (no_message))
+ {
+ /* It has shrunk too much; turn off auto-saving here. */
+ message ("Buffer %s has shrunk a lot; auto save turned off there",
+ XSTRING (b->name)->data);
+ /* User can reenable saving with M-x auto-save. */
+ b->auto_save_file_name = Qnil;
+ /* Prevent warning from repeating if user does so. */
+ XFASTINT (b->save_length) = 0;
+ Fsleep_for (make_number (1), Qnil);
+ continue;
+ }
+ set_buffer_internal (b);
+ if (!auto_saved && NILP (no_message))
+ message1 ("Auto-saving...");
+ internal_condition_case (auto_save_1, Qt, auto_save_error);
+ auto_saved++;
+ b->auto_save_modified = BUF_MODIFF (b);
+ XFASTINT (current_buffer->save_length) = Z - BEG;
+ set_buffer_internal (old);
+ }
+ }
/* Prevent another auto save till enough input events come in. */
record_auto_save ();
- if (auto_saved && NILP (nomsg))
+ if (auto_saved && NILP (no_message))
message1 (omessage ? omessage : "Auto-saving...done");
+ Vquit_flag = oquit;
+
auto_saving = 0;
return Qnil;
}
tem = Fstring_equal (val, insdef);
if (!NILP (tem) && !NILP (defalt))
return defalt;
+ if (XSTRING (val)->size == 0 && NILP (insdef))
+ return defalt;
return Fsubstitute_in_file_name (val);
}
Qinsert_file_contents = intern ("insert-file-contents");
Qwrite_region = intern ("write-region");
Qverify_visited_file_modtime = intern ("verify-visited-file-modtime");
+ Qset_visited_file_modtime = intern ("set-visited-file-modtime");
staticpro (&Qexpand_file_name);
staticpro (&Qdirectory_file_name);
Qfile_already_exists = intern("file-already-exists");
staticpro (&Qfile_already_exists);
+ Qcar_less_than_car = intern ("car-less-than-car");
+ staticpro (&Qcar_less_than_car);
+
Fput (Qfile_error, Qerror_conditions,
Fcons (Qfile_error, Fcons (Qerror, Qnil)));
Fput (Qfile_error, Qerror_message,
for its argument.");
Vfile_name_handler_alist = Qnil;
+ DEFVAR_LISP ("after-insert-file-functions", &Vafter_insert_file_functions,
+ "A list of functions to be called at the end of `insert-file-contents'.\n\
+Each is passed one argument, the number of bytes inserted. It should return\n\
+the new byte count, and leave point the same. If `insert-file-contents' is\n\
+intercepted by a handler from `file-name-handler-alist', that handler is\n\
+responsible for calling the after-insert-file-functions if appropriate.");
+ Vafter_insert_file_functions = Qnil;
+
+ DEFVAR_LISP ("write-region-annotate-functions", &Vwrite_region_annotate_functions,
+ "A list of functions to be called at the start of `write-region'.\n\
+Each is passed two arguments, START and END as for `write-region'. It should\n\
+return a list of pairs (POSITION . STRING) of strings to be effectively\n\
+inserted at the specified positions of the file being written (1 means to\n\
+insert before the first byte written). The POSITIONs must be sorted into\n\
+increasing order. If there are several functions in the list, the several\n\
+lists are merged destructively.");
+ Vwrite_region_annotate_functions = Qnil;
+
defsubr (&Sfind_file_name_handler);
defsubr (&Sfile_name_directory);
defsubr (&Sfile_name_nondirectory);
defsubr (&Sfile_newer_than_file_p);
defsubr (&Sinsert_file_contents);
defsubr (&Swrite_region);
+ defsubr (&Scar_less_than_car);
defsubr (&Sverify_visited_file_modtime);
defsubr (&Sclear_visited_file_modtime);
+ defsubr (&Svisited_file_modtime);
defsubr (&Sset_visited_file_modtime);
defsubr (&Sdo_auto_save);
defsubr (&Sset_buffer_auto_saved);