/* File IO for GNU Emacs.
-Copyright (C) 1985-1988, 1993-2012 Free Software Foundation, Inc.
+Copyright (C) 1985-1988, 1993-2013 Free Software Foundation, Inc.
This file is part of GNU Emacs.
/* Set by auto_save_1 if an error occurred during the last auto-save. */
static bool auto_save_error_occurred;
+/* If VALID_TIMESTAMP_FILE_SYSTEM, then TIMESTAMP_FILE_SYSTEM is the device
+ number of a file system where time stamps were observed to to work. */
+static bool valid_timestamp_file_system;
+static dev_t timestamp_file_system;
+
/* The symbol bound to coding-system-for-read when
insert-file-contents is called for recovering a file. This is not
an actual coding system name, but just an indicator to tell
strcat (res, "/");
beg = res;
p = beg + strlen (beg);
+ dostounix_filename (beg);
+ tem_fn = make_specified_string (beg, -1, p - beg,
+ STRING_MULTIBYTE (filename));
}
+ else
+ tem_fn = make_specified_string (beg - 2, -1, p - beg + 2,
+ STRING_MULTIBYTE (filename));
+ }
+ else if (STRING_MULTIBYTE (filename))
+ {
+ tem_fn = ENCODE_FILE (make_specified_string (beg, -1, p - beg, 1));
+ dostounix_filename (SSDATA (tem_fn));
+ tem_fn = DECODE_FILE (tem_fn);
}
- tem_fn = ENCODE_FILE (make_specified_string (beg, -1, p - beg,
- STRING_MULTIBYTE (filename)));
- dostounix_filename (SSDATA (tem_fn));
- return DECODE_FILE (tem_fn);
+ else
+ {
+ dostounix_filename (beg);
+ tem_fn = make_specified_string (beg, -1, p - beg, 0);
+ }
+ return tem_fn;
#else /* DOS_NT */
return make_specified_string (beg, -1, p - beg, STRING_MULTIBYTE (filename));
#endif /* DOS_NT */
return Ffile_name_directory (filename);
}
-/* Convert from file name SRC of length SRCLEN to directory name
- in DST. On UNIX, just make sure there is a terminating /.
- Return the length of DST in bytes. */
+/* Convert from file name SRC of length SRCLEN to directory name in
+ DST. MULTIBYTE non-zero means the file name in SRC is a multibyte
+ string. On UNIX, just make sure there is a terminating /. Return
+ the length of DST in bytes. */
static ptrdiff_t
-file_name_as_directory (char *dst, const char *src, ptrdiff_t srclen)
+file_name_as_directory (char *dst, const char *src, ptrdiff_t srclen,
+ bool multibyte)
{
if (srclen == 0)
{
srclen++;
}
#ifdef DOS_NT
- {
- Lisp_Object tem_fn = make_specified_string (dst, -1, srclen, 1);
+ if (multibyte)
+ {
+ Lisp_Object tem_fn = make_specified_string (dst, -1, srclen, 1);
- tem_fn = ENCODE_FILE (tem_fn);
- dostounix_filename (SSDATA (tem_fn));
- tem_fn = DECODE_FILE (tem_fn);
- memcpy (dst, SSDATA (tem_fn), (srclen = SBYTES (tem_fn)) + 1);
- }
+ tem_fn = ENCODE_FILE (tem_fn);
+ dostounix_filename (SSDATA (tem_fn));
+ tem_fn = DECODE_FILE (tem_fn);
+ memcpy (dst, SSDATA (tem_fn), (srclen = SBYTES (tem_fn)) + 1);
+ }
+ else
+ dostounix_filename (dst);
#endif
return srclen;
}
}
buf = alloca (SBYTES (file) + 10);
- length = file_name_as_directory (buf, SSDATA (file), SBYTES (file));
+ length = file_name_as_directory (buf, SSDATA (file), SBYTES (file),
+ STRING_MULTIBYTE (file));
return make_specified_string (buf, -1, length, STRING_MULTIBYTE (file));
}
\f
-/* Convert from directory name SRC of length SRCLEN to
- file name in DST. On UNIX, just make sure there isn't
- a terminating /. Return the length of DST in bytes. */
+/* Convert from directory name SRC of length SRCLEN to file name in
+ DST. MULTIBYTE non-zero means the file name in SRC is a multibyte
+ string. On UNIX, just make sure there isn't a terminating /.
+ Return the length of DST in bytes. */
static ptrdiff_t
-directory_file_name (char *dst, char *src, ptrdiff_t srclen)
+directory_file_name (char *dst, char *src, ptrdiff_t srclen, bool multibyte)
{
/* Process as Unix format: just remove any final slash.
But leave "/" unchanged; do not change it to "". */
srclen--;
}
#ifdef DOS_NT
- {
- Lisp_Object tem_fn = make_specified_string (dst, -1, srclen, 1);
+ if (multibyte)
+ {
+ Lisp_Object tem_fn = make_specified_string (dst, -1, srclen, 1);
- tem_fn = ENCODE_FILE (tem_fn);
- dostounix_filename (SSDATA (tem_fn));
- tem_fn = DECODE_FILE (tem_fn);
- memcpy (dst, SSDATA (tem_fn), (srclen = SBYTES (tem_fn)) + 1);
- }
+ tem_fn = ENCODE_FILE (tem_fn);
+ dostounix_filename (SSDATA (tem_fn));
+ tem_fn = DECODE_FILE (tem_fn);
+ memcpy (dst, SSDATA (tem_fn), (srclen = SBYTES (tem_fn)) + 1);
+ }
+ else
+ dostounix_filename (dst);
#endif
return srclen;
}
}
buf = alloca (SBYTES (directory) + 20);
- length = directory_file_name (buf, SSDATA (directory), SBYTES (directory));
+ length = directory_file_name (buf, SSDATA (directory), SBYTES (directory),
+ STRING_MULTIBYTE (directory));
return make_specified_string (buf, -1, length, STRING_MULTIBYTE (directory));
}
/* `egetenv' may return a unibyte string, which will bite us since
we expect the directory to be multibyte. */
tem = build_string (newdir);
- if (!STRING_MULTIBYTE (tem))
+ if (multibyte && !STRING_MULTIBYTE (tem))
{
hdir = DECODE_FILE (tem);
newdir = SSDATA (hdir);
unblock_input ();
if (pw)
{
+ Lisp_Object tem;
+
newdir = pw->pw_dir;
+ /* `getpwnam' may return a unibyte string, which will
+ bite us since we expect the directory to be
+ multibyte. */
+ tem = build_string (newdir);
+ if (multibyte && !STRING_MULTIBYTE (tem))
+ {
+ hdir = DECODE_FILE (tem);
+ newdir = SSDATA (hdir);
+ }
nm = p;
#ifdef DOS_NT
collapse_newdir = 0;
adir = alloca (MAXPATHLEN + 1);
if (!getdefdir (c_toupper (drive) - 'A' + 1, adir))
adir = NULL;
+ else if (multibyte)
+ {
+ Lisp_Object tem = build_string (adir);
+
+ tem = DECODE_FILE (tem);
+ memcpy (adir, SSDATA (tem), SBYTES (tem) + 1);
+ }
}
if (!adir)
{
indirectly by prepending newdir to nm if necessary, and using
cwd (or the wd of newdir's drive) as the new newdir. */
char *adir;
+
if (IS_DRIVE (newdir[0]) && IS_DEVICE_SEP (newdir[1]))
{
drive = (unsigned char) newdir[0];
{
ptrdiff_t newlen = strlen (newdir);
char *tmp = alloca (newlen + strlen (nm) + 2);
- file_name_as_directory (tmp, newdir, newlen);
+ file_name_as_directory (tmp, newdir, newlen, multibyte);
strcat (tmp, nm);
nm = tmp;
}
if (drive)
{
if (!getdefdir (c_toupper (drive) - 'A' + 1, adir))
- newdir = "/";
+ strcpy (adir, "/");
}
else
getcwd (adir, MAXPATHLEN + 1);
+ if (multibyte)
+ {
+ Lisp_Object tem = build_string (adir);
+
+ tem = DECODE_FILE (tem);
+ memcpy (adir, SSDATA (tem), SBYTES (tem) + 1);
+ }
newdir = adir;
}
strcpy (target, newdir);
}
else
- file_name_as_directory (target, newdir, length);
+ file_name_as_directory (target, newdir, length, multibyte);
}
strcat (target, nm);
target[1] = ':';
}
result = make_specified_string (target, -1, o - target, multibyte);
- result = ENCODE_FILE (result);
- dostounix_filename (SSDATA (result));
- result = DECODE_FILE (result);
+ if (multibyte)
+ {
+ result = ENCODE_FILE (result);
+ dostounix_filename (SSDATA (result));
+ result = DECODE_FILE (result);
+ }
+ else
+ dostounix_filename (SSDATA (result));
#else /* !DOS_NT */
result = make_specified_string (target, -1, o - target, multibyte);
#endif /* !DOS_NT */
memcpy (nm, SDATA (filename), SBYTES (filename) + 1);
#ifdef DOS_NT
- {
- Lisp_Object encoded_filename = ENCODE_FILE (filename);
- Lisp_Object tem_fn;
-
- dostounix_filename (SDATA (encoded_filename));
- tem_fn = DECODE_FILE (encoded_filename);
- nm = alloca (SBYTES (tem_fn) + 1);
- memcpy (nm, SDATA (tem_fn), SBYTES (tem_fn) + 1);
- substituted = (memcmp (nm, SDATA (filename), SBYTES (filename)) != 0);
- if (substituted)
- filename = tem_fn;
- }
+ if (multibyte)
+ {
+ Lisp_Object encoded_filename = ENCODE_FILE (filename);
+ Lisp_Object tem_fn;
+
+ dostounix_filename (SDATA (encoded_filename));
+ tem_fn = DECODE_FILE (encoded_filename);
+ nm = alloca (SBYTES (tem_fn) + 1);
+ memcpy (nm, SDATA (tem_fn), SBYTES (tem_fn) + 1);
+ substituted = (memcmp (nm, SDATA (filename), SBYTES (filename)) != 0);
+ if (substituted)
+ filename = tem_fn;
+ }
+ else
+ {
+ dostounix_filename (nm);
+ substituted = (memcmp (nm, SDATA (filename), SBYTES (filename)) != 0);
+ }
#endif
endp = nm + SBYTES (filename);
out_st.st_mode = 0;
#ifdef WINDOWSNT
+ if (!NILP (preserve_extended_attributes))
+ {
+#ifdef HAVE_POSIX_ACL
+ acl = acl_get_file (SDATA (encoded_file), ACL_TYPE_ACCESS);
+ if (acl == NULL && errno != ENOTSUP)
+ report_file_error ("Getting ACL", Fcons (file, Qnil));
+#endif
+ }
if (!CopyFile (SDATA (encoded_file),
SDATA (encoded_newname),
FALSE))
- report_file_error ("Copying file", Fcons (file, Fcons (newname, Qnil)));
+ {
+ /* CopyFile doesn't set errno when it fails. By far the most
+ "popular" reason is that the target is read-only. */
+ if (GetLastError () == 5)
+ errno = EACCES;
+ else
+ errno = EPERM;
+ report_file_error ("Copying file", Fcons (file, Fcons (newname, Qnil)));
+ }
/* CopyFile retains the timestamp by default. */
else if (NILP (keep_time))
{
/* Restore original attributes. */
SetFileAttributes (filename, attributes);
}
+#ifdef HAVE_POSIX_ACL
+ if (acl != NULL)
+ {
+ bool fail =
+ acl_set_file (SDATA (encoded_newname), ACL_TYPE_ACCESS, acl) != 0;
+ if (fail && errno != ENOTSUP)
+ report_file_error ("Setting ACL", Fcons (newname, Qnil));
+
+ acl_free (acl);
+ }
+#endif
#else /* not WINDOWSNT */
immediate_quit = 1;
ifd = emacs_open (SSDATA (encoded_file), O_RDONLY, 0);
CONTEXT should be a list (USER ROLE TYPE RANGE), where the list
elements are strings naming the components of a SELinux context.
-This function does nothing if SELinux is disabled, or if Emacs was not
-compiled with SELinux support. */)
+Value is t if setting of SELinux context was successful, nil otherwise.
+
+This function does nothing and returns nil if SELinux is disabled,
+or if Emacs was not compiled with SELinux support. */)
(Lisp_Object filename, Lisp_Object context)
{
Lisp_Object absname;
context_free (parsed_con);
freecon (con);
+ return fail ? Qnil : Qt;
}
else
report_file_error ("Doing lgetfilecon", Fcons (absname, Qnil));
}
\f
DEFUN ("file-acl", Ffile_acl, Sfile_acl, 1, 1, 0,
- doc: /* Return ACL entries of file named FILENAME, as a string.
+ doc: /* Return ACL entries of file named FILENAME.
+The entries are returned in a format suitable for use in `set-file-acl'
+but is otherwise undocumented and subject to change.
Return nil if file does not exist or is not accessible, or if Emacs
-was unable to determine the ACL entries. The latter can happen for
-local files if Emacs was not compiled with ACL support, or for remote
-files if the file handler returns nil for the file's ACL entries. */)
+was unable to determine the ACL entries. */)
(Lisp_Object filename)
{
Lisp_Object absname;
ACL-STRING should contain the textual representation of the ACL
entries in a format suitable for the platform.
+Value is t if setting of ACL was successful, nil otherwise.
+
Setting ACL for local files requires Emacs to be built with ACL
support. */)
(Lisp_Object filename, Lisp_Object acl_string)
report_file_error ("Setting ACL", Fcons (absname, Qnil));
acl_free (acl);
+ return fail ? Qnil : Qt;
}
#endif
return Qnil;
}
-
-/* Used to pass values from insert-file-contents to read_non_regular. */
-
-static int non_regular_fd;
-static ptrdiff_t non_regular_inserted;
-static int non_regular_nbytes;
-
-
-/* Read from a non-regular file.
- Read non_regular_nbytes bytes max from non_regular_fd.
- Non_regular_inserted specifies where to put the read bytes.
- Value is the number of bytes read. */
+/* Check quit and read from the file. STATE is a Lisp_Save_Value
+ object where slot 0 is the file descriptor, slot 1 specifies
+ an offset to put the read bytes, and slot 2 is the maximum
+ amount of bytes to read. Value is the number of bytes read. */
static Lisp_Object
-read_non_regular (Lisp_Object ignore)
+read_contents (Lisp_Object state)
{
int nbytes;
immediate_quit = 1;
QUIT;
- nbytes = emacs_read (non_regular_fd,
+ nbytes = emacs_read (XSAVE_INTEGER (state, 0),
((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
- + non_regular_inserted),
- non_regular_nbytes);
+ + XSAVE_INTEGER (state, 1)),
+ XSAVE_INTEGER (state, 2));
immediate_quit = 0;
+ /* Fast recycle this object for the likely next call. */
+ free_misc (state);
return make_number (nbytes);
}
-
-/* Condition-case handler used when reading from non-regular files
- in insert-file-contents. */
+/* Condition-case handler used when reading files in insert-file-contents. */
static Lisp_Object
-read_non_regular_quit (Lisp_Object ignore)
+read_contents_quit (Lisp_Object ignore)
{
return Qnil;
}
-/* Reposition FD to OFFSET, based on WHENCE. This acts like lseek
- except that it also tests for OFFSET being out of lseek's range. */
+/* Return the file offset that VAL represents, checking for type
+ errors and overflow. */
static off_t
-emacs_lseek (int fd, EMACS_INT offset, int whence)
+file_offset (Lisp_Object val)
{
- /* Use "&" rather than "&&" to suppress a bogus GCC warning; see
- <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43772>. */
- if (! ((offset >= TYPE_MINIMUM (off_t)) & (offset <= TYPE_MAXIMUM (off_t))))
+ if (RANGED_INTEGERP (0, val, TYPE_MAXIMUM (off_t)))
+ return XINT (val);
+
+ if (FLOATP (val))
{
- errno = EINVAL;
- return -1;
+ double v = XFLOAT_DATA (val);
+ if (0 <= v
+ && (sizeof (off_t) < sizeof v
+ ? v <= TYPE_MAXIMUM (off_t)
+ : v < TYPE_MAXIMUM (off_t)))
+ return v;
}
- return lseek (fd, offset, whence);
+
+ wrong_type_argument (intern ("file-offset"), val);
}
/* Return a special time value indicating the error number ERRNUM. */
This function does code conversion according to the value of
`coding-system-for-read' or `file-coding-system-alist', and sets the
-variable `last-coding-system-used' to the coding system actually used. */)
+variable `last-coding-system-used' to the coding system actually used.
+
+In addition, this function decodes the inserted text from known formats
+by calling `format-decode', which see. */)
(Lisp_Object filename, Lisp_Object visit, Lisp_Object beg, Lisp_Object end, Lisp_Object replace)
{
struct stat st;
- int file_status;
EMACS_TIME mtime;
int fd;
ptrdiff_t inserted = 0;
Lisp_Object p;
ptrdiff_t total = 0;
bool not_regular = 0;
- int save_errno = 0;
+ int save_errno = 0, read_errno = 0;
char read_buf[READ_BUF_SIZE];
struct coding_system coding;
char buffer[1 << 14];
orig_filename = filename;
filename = ENCODE_FILE (filename);
- fd = -1;
-
-#ifdef WINDOWSNT
- {
- Lisp_Object tem = Vw32_get_true_file_attributes;
-
- /* Tell stat to use expensive method to get accurate info. */
- Vw32_get_true_file_attributes = Qt;
- file_status = stat (SSDATA (filename), &st);
- Vw32_get_true_file_attributes = tem;
- }
-#else
- file_status = stat (SSDATA (filename), &st);
-#endif /* WINDOWSNT */
-
- if (file_status == 0)
- mtime = get_stat_mtime (&st);
- else
+ fd = emacs_open (SSDATA (filename), O_RDONLY, 0);
+ if (fd < 0)
{
- badopen:
save_errno = errno;
if (NILP (visit))
report_file_error ("Opening input file", Fcons (orig_filename, Qnil));
goto notfound;
}
+ /* Replacement should preserve point as it preserves markers. */
+ if (!NILP (replace))
+ record_unwind_protect (restore_point_unwind, Fpoint_marker ());
+
+ record_unwind_protect (close_file_unwind, make_number (fd));
+
+ if (fstat (fd, &st) != 0)
+ report_file_error ("Input file status", Fcons (orig_filename, Qnil));
+ mtime = get_stat_mtime (&st);
+
/* This code will need to be changed in order to work on named
pipes, and it's probably just not worth it. So we should at
least signal an error. */
build_string ("not a regular file"), orig_filename);
}
- if (fd < 0)
- if ((fd = emacs_open (SSDATA (filename), O_RDONLY, 0)) < 0)
- goto badopen;
-
- /* Replacement should preserve point as it preserves markers. */
- if (!NILP (replace))
- record_unwind_protect (restore_point_unwind, Fpoint_marker ());
-
- record_unwind_protect (close_file_unwind, make_number (fd));
-
-
if (!NILP (visit))
{
if (!NILP (beg) || !NILP (end))
}
if (!NILP (beg))
- {
- if (! RANGED_INTEGERP (0, beg, TYPE_MAXIMUM (off_t)))
- wrong_type_argument (intern ("file-offset"), beg);
- beg_offset = XFASTINT (beg);
- }
+ beg_offset = file_offset (beg);
else
beg_offset = 0;
if (!NILP (end))
- {
- if (! RANGED_INTEGERP (0, end, TYPE_MAXIMUM (off_t)))
- wrong_type_argument (intern ("file-offset"), end);
- end_offset = XFASTINT (end);
- }
+ end_offset = file_offset (end);
else
{
if (not_regular)
prepare_to_modify_buffer (GPT, GPT, NULL);
}
- move_gap (PT);
+ move_gap_both (PT, PT_BYTE);
if (GAP_SIZE < total)
make_gap (total - GAP_SIZE);
Fcons (orig_filename, Qnil));
}
- /* In the following loop, HOW_MUCH contains the total bytes read so
- far for a regular file, and not changed for a special file. But,
- before exiting the loop, it is set to a negative value if I/O
- error occurs. */
+ /* In the following loop, HOW_MUCH contains the total bytes read
+ so far for a regular file, and not changed for a special file. */
how_much = 0;
/* Total bytes inserted. */
inserted = 0;
- /* Here, we don't do code conversion in the loop. It is done by
- decode_coding_gap after all data are read into the buffer. */
- {
- ptrdiff_t gap_size = GAP_SIZE;
-
- while (how_much < total)
- {
- /* try is reserved in some compilers (Microsoft C) */
- int trytry = min (total - how_much, READ_BUF_SIZE);
- ptrdiff_t this;
+ /* Here we don't do code conversion in the loop. It is done by
+ decode_coding_gap after all data are read into the buffer, or
+ reading loop is interrupted with quit or due to I/O error. */
- if (not_regular)
- {
- Lisp_Object nbytes;
-
- /* Maybe make more room. */
- if (gap_size < trytry)
- {
- make_gap (total - gap_size);
- gap_size = GAP_SIZE;
- }
-
- /* Read from the file, capturing `quit'. When an
- error occurs, end the loop, and arrange for a quit
- to be signaled after decoding the text we read. */
- non_regular_fd = fd;
- non_regular_inserted = inserted;
- non_regular_nbytes = trytry;
- nbytes = internal_condition_case_1 (read_non_regular,
- Qnil, Qerror,
- read_non_regular_quit);
- if (NILP (nbytes))
- {
- read_quit = 1;
- break;
- }
-
- this = XINT (nbytes);
- }
- else
- {
- /* Allow quitting out of the actual I/O. We don't make text
- part of the buffer until all the reading is done, so a C-g
- here doesn't do any harm. */
- immediate_quit = 1;
- QUIT;
- this = emacs_read (fd,
- ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
- + inserted),
- trytry);
- immediate_quit = 0;
- }
-
- if (this <= 0)
- {
- how_much = this;
- break;
- }
-
- gap_size -= this;
+ while (how_much < total)
+ {
+ ptrdiff_t nread, maxread = min (total - how_much, READ_BUF_SIZE);
+ Lisp_Object result;
+
+ /* For a special file, gap is enlarged as we read,
+ so GAP_SIZE should be checked every time. */
+ if (not_regular && (GAP_SIZE < maxread))
+ make_gap (maxread - GAP_SIZE);
+
+ /* Read from the file, capturing `quit'. */
+ result = internal_condition_case_1
+ (read_contents,
+ make_save_value ("iii", (ptrdiff_t) fd, inserted, maxread),
+ Qerror, read_contents_quit);
+ if (NILP (result))
+ {
+ /* Quit is signaled. End the loop and arrange
+ real quit after decoding the text we read. */
+ read_quit = 1;
+ break;
+ }
+ nread = XINT (result);
+ if (nread <= 0)
+ {
+ /* End of file or I/O error. End the loop and
+ save error code in case of I/O error. */
+ if (nread < 0)
+ read_errno = errno;
+ break;
+ }
- /* For a regular file, where TOTAL is the real size,
- count HOW_MUCH to compare with it.
- For a special file, where TOTAL is just a buffer size,
- so don't bother counting in HOW_MUCH.
- (INSERTED is where we count the number of characters inserted.) */
- if (! not_regular)
- how_much += this;
- inserted += this;
- }
- }
+ /* Adjust gap and end positions. */
+ GAP_SIZE -= nread;
+ GPT += nread;
+ ZV += nread;
+ Z += nread;
+ GPT_BYTE += nread;
+ ZV_BYTE += nread;
+ Z_BYTE += nread;
+ if (GAP_SIZE > 0)
+ *(GPT_ADDR) = 0;
+
+ /* For a regular file, where TOTAL is the real size, count HOW_MUCH to
+ compare with it. For a special file, where TOTAL is just a buffer
+ size, don't bother counting in HOW_MUCH, but always accumulate the
+ number of bytes read in INSERTED. */
+ if (!not_regular)
+ how_much += nread;
+ inserted += nread;
+ }
- /* Now we have read all the file data into the gap.
- If it was empty, undo marking the buffer modified. */
+ /* Now we have either read all the file data into the gap,
+ or stop reading on I/O error or quit. If nothing was
+ read, undo marking the buffer modified. */
if (inserted == 0)
{
else
Vdeactivate_mark = Qt;
- /* Make the text read part of the buffer. */
- GAP_SIZE -= inserted;
- GPT += inserted;
- GPT_BYTE += inserted;
- ZV += inserted;
- ZV_BYTE += inserted;
- Z += inserted;
- Z_BYTE += inserted;
-
- if (GAP_SIZE > 0)
- /* Put an anchor to ensure multi-byte form ends at gap. */
- *GPT_ADDR = 0;
-
emacs_close (fd);
/* Discard the unwind protect for closing the file. */
specpdl_ptr--;
- if (how_much < 0)
- error ("IO error reading %s: %s",
- SDATA (orig_filename), emacs_strerror (errno));
-
notfound:
if (NILP (coding_system))
report_file_error ("Opening input file", Fcons (orig_filename, Qnil));
}
+ /* There was an error reading file. */
+ if (read_errno)
+ error ("IO error reading %s: %s",
+ SDATA (orig_filename), emacs_strerror (read_errno));
+
+ /* Quit was signaled. */
if (read_quit)
Fsignal (Qquit, Qnil);
- /* ??? Retval needs to be dealt with in all cases consistently. */
if (NILP (val))
- val = Fcons (orig_filename,
- Fcons (make_number (inserted),
- Qnil));
+ val = list2 (orig_filename, make_number (inserted));
RETURN_UNGCPRO (unbind_to (count, val));
}
instead of any buffer contents; END is ignored.
Optional fourth argument APPEND if non-nil means
- append to existing file contents (if any). If it is an integer,
+ append to existing file contents (if any). If it is a number,
seek to that offset in the file before writing.
Optional fifth argument VISIT, if t or a string, means
set the last-save-file-modtime of buffer to this file's modtime
(Lisp_Object start, Lisp_Object end, Lisp_Object filename, Lisp_Object append, Lisp_Object visit, Lisp_Object lockname, Lisp_Object mustbenew)
{
int desc;
+ int open_flags;
+ int mode;
+ off_t offset IF_LINT (= 0);
bool ok;
int save_errno = 0;
const char *fn;
#endif /* CLASH_DETECTION */
encoded_filename = ENCODE_FILE (filename);
-
fn = SSDATA (encoded_filename);
- desc = -1;
- if (!NILP (append))
+ open_flags = O_WRONLY | O_BINARY | O_CREAT;
+ open_flags |= EQ (mustbenew, Qexcl) ? O_EXCL : !NILP (append) ? 0 : O_TRUNC;
+ if (NUMBERP (append))
+ offset = file_offset (append);
+ else if (!NILP (append))
+ open_flags |= O_APPEND;
#ifdef DOS_NT
- desc = emacs_open (fn, O_WRONLY | O_BINARY, 0);
-#else /* not DOS_NT */
- desc = emacs_open (fn, O_WRONLY, 0);
-#endif /* not DOS_NT */
+ mode = S_IREAD | S_IWRITE;
+#else
+ mode = auto_saving ? auto_save_mode_bits : 0666;
+#endif
- if (desc < 0 && (NILP (append) || errno == ENOENT))
-#ifdef DOS_NT
- desc = emacs_open (fn,
- O_WRONLY | O_CREAT | O_BINARY
- | (EQ (mustbenew, Qexcl) ? O_EXCL : O_TRUNC),
- S_IREAD | S_IWRITE);
-#else /* not DOS_NT */
- desc = emacs_open (fn, O_WRONLY | O_TRUNC | O_CREAT
- | (EQ (mustbenew, Qexcl) ? O_EXCL : 0),
- auto_saving ? auto_save_mode_bits : 0666);
-#endif /* not DOS_NT */
+ desc = emacs_open (fn, open_flags, mode);
if (desc < 0)
{
record_unwind_protect (close_file_unwind, make_number (desc));
- if (!NILP (append) && !NILP (Ffile_regular_p (filename)))
+ if (NUMBERP (append))
{
- off_t ret;
-
- if (NUMBERP (append))
- ret = emacs_lseek (desc, XINT (append), SEEK_CUR);
- else
- ret = lseek (desc, 0, SEEK_END);
+ off_t ret = lseek (desc, offset, SEEK_SET);
if (ret < 0)
{
#ifdef CLASH_DETECTION
/* Discard the unwind protect for close_file_unwind. */
specpdl_ptr = specpdl + count1;
+ /* Some file systems have a bug where st_mtime is not updated
+ properly after a write. For example, CIFS might not see the
+ st_mtime change until after the file is opened again.
+
+ Attempt to detect this file system bug, and update MODTIME to the
+ newer st_mtime if the bug appears to be present. This introduces
+ a race condition, so to avoid most instances of the race condition
+ on non-buggy file systems, skip this check if the most recently
+ encountered non-buggy file system was the current file system.
+
+ A race condition can occur if some other process modifies the
+ file between the fstat above and the fstat below, but the race is
+ unlikely and a similar race between the last write and the fstat
+ above cannot possibly be closed anyway. */
+
+ if (EMACS_TIME_VALID_P (modtime)
+ && ! (valid_timestamp_file_system && st.st_dev == timestamp_file_system))
+ {
+ int desc1 = emacs_open (fn, O_WRONLY | O_BINARY, 0);
+ if (0 <= desc1)
+ {
+ struct stat st1;
+ if (fstat (desc1, &st1) == 0
+ && st.st_dev == st1.st_dev && st.st_ino == st1.st_ino)
+ {
+ EMACS_TIME modtime1 = get_stat_mtime (&st1);
+ if (EMACS_TIME_EQ (modtime, modtime1)
+ && st.st_size == st1.st_size)
+ {
+ timestamp_file_system = st.st_dev;
+ valid_timestamp_file_system = 1;
+ }
+ else
+ {
+ st.st_size = st1.st_size;
+ modtime = modtime1;
+ }
+ }
+ emacs_close (desc1);
+ }
+ }
+
/* Call write-region-post-annotation-function. */
while (CONSP (Vwrite_region_annotation_buffers))
{
}
if (!auto_saving)
- message_with_string ((INTEGERP (append)
+ message_with_string ((NUMBERP (append)
? "Updated %s"
: ! NILP (append)
? "Added to %s"
do_auto_save_unwind (Lisp_Object arg) /* used as unwind-protect function */
{
- FILE *stream = (FILE *) XSAVE_VALUE (arg)->pointer;
+ FILE *stream = XSAVE_POINTER (arg, 0);
auto_saving = 0;
if (stream != NULL)
{
}
record_unwind_protect (do_auto_save_unwind,
- make_save_value (stream, 0));
+ make_save_pointer (stream));
record_unwind_protect (do_auto_save_unwind_1,
make_number (minibuffer_auto_raise));
minibuffer_auto_raise = 0;
}
\f
+void
+init_fileio (void)
+{
+ valid_timestamp_file_system = 0;
+}
+
void
syms_of_fileio (void)
{