/* File IO for GNU Emacs.
- Copyright (C) 1985,86,87,88,93,94,95,96,97,98,99,2000
+ Copyright (C) 1985,86,87,88,93,94,95,96,97,98,99,2000, 2001
Free Software Foundation, Inc.
This file is part of GNU Emacs.
}
#endif
- /* If nm is absolute, look for /./ or /../ sequences; if none are
- found, we can probably return right away. We will avoid allocating
- a new string if name is already fully expanded. */
+ /* If nm is absolute, look for `/./' or `/../' or `//''sequences; if
+ none are found, we can probably return right away. We will avoid
+ allocating a new string if name is already fully expanded. */
if (
IS_DIRECTORY_SEP (nm[0])
#ifdef MSDOS
|| (p[2] == '.' && (IS_DIRECTORY_SEP (p[3])
|| p[3] == 0))))
lose = 1;
+ /* We want to replace multiple `/' in a row with a single
+ slash. */
+ else if (p > nm
+ && IS_DIRECTORY_SEP (p[0])
+ && IS_DIRECTORY_SEP (p[1]))
+ lose = 1;
+
#ifdef VMS
if (p[0] == '\\')
lose = 1;
/* ASSERT (IS_DIRECTORY_SEP (target[0])) if not VMS */
- /* Now canonicalize by removing /. and /foo/.. if they appear. */
+ /* Now canonicalize by removing `//', `/.' and `/foo/..' if they
+ appear. */
p = target;
o = target;
++o;
p += 3;
}
+ else if (p > target
+ && IS_DIRECTORY_SEP (p[0]) && IS_DIRECTORY_SEP (p[1]))
+ {
+ /* Collapse multiple `/' in a row. */
+ *o++ = *p++;
+ while (IS_DIRECTORY_SEP (*p))
+ ++p;
+ }
else
{
*o++ = *p++;
Fourth arg KEEP-TIME non-nil means give the new file the same\n\
last-modified time as the old one. (This works on only some systems.)\n\
A prefix arg makes KEEP-TIME non-nil.")
- (file, newname, ok_if_already_exists, keep_date)
- Lisp_Object file, newname, ok_if_already_exists, keep_date;
+ (file, newname, ok_if_already_exists, keep_time)
+ Lisp_Object file, newname, ok_if_already_exists, keep_time;
{
int ifd, ofd, n;
char buf[16 * 1024];
handler = Ffind_file_name_handler (newname, Qcopy_file);
if (!NILP (handler))
RETURN_UNGCPRO (call5 (handler, Qcopy_file, file, newname,
- ok_if_already_exists, keep_date));
+ ok_if_already_exists, keep_time));
encoded_file = ENCODE_FILE (file);
encoded_newname = ENCODE_FILE (newname);
else if (stat (XSTRING (encoded_newname)->data, &out_st) < 0)
out_st.st_mode = 0;
+#ifdef WINDOWSNT
+ if (!CopyFile (XSTRING (encoded_file)->data,
+ XSTRING (encoded_newname)->data,
+ FALSE))
+ report_file_error ("Copying file", Fcons (file, Fcons (newname, Qnil)));
+ else if (NILP (keep_time))
+ {
+ EMACS_TIME now;
+ EMACS_GET_TIME (now);
+ if (set_file_times (XSTRING (encoded_newname)->data,
+ now, now))
+ Fsignal (Qfile_date_error,
+ Fcons (build_string ("Cannot set file date"),
+ Fcons (newname, Qnil)));
+ }
+#else /* not WINDOWSNT */
ifd = emacs_open (XSTRING (encoded_file)->data, O_RDONLY, 0);
if (ifd < 0)
report_file_error ("Opening input file", Fcons (file, Qnil));
if (input_file_statable_p)
{
- if (!NILP (keep_date))
+ if (!NILP (keep_time))
{
EMACS_TIME atime, mtime;
EMACS_SET_SECS_USECS (atime, st.st_atime, 0);
}
emacs_close (ifd);
+#endif /* WINDOWSNT */
/* Discard the unwind protects. */
specpdl_ptr = specpdl + count;
absname = ENCODE_FILE (absname);
-#ifdef DOS_NT
- /* Under MS-DOS and Windows, open does not work for directories. */
+#if defined(DOS_NT) || defined(macintosh)
+ /* Under MS-DOS, Windows, and Macintosh, open does not work for
+ directories. */
if (access (XSTRING (absname)->data, 0) == 0)
return Qt;
return Qnil;
-#else /* not DOS_NT */
+#else /* not DOS_NT and not macintosh */
flags = O_RDONLY;
#if defined (S_ISFIFO) && defined (O_NONBLOCK)
/* Opening a fifo without O_NONBLOCK can wait.
return Qnil;
emacs_close (desc);
return Qt;
-#endif /* not DOS_NT */
+#endif /* not DOS_NT and not macintosh */
}
/* Having this before file-symlink-p mysteriously caused it to be forgotten
filename = ENCODE_FILE (filename);
- bufsize = 100;
- while (1)
+ bufsize = 50;
+ buf = NULL;
+ do
{
- buf = (char *) xmalloc (bufsize);
+ bufsize *= 2;
+ buf = (char *) xrealloc (buf, bufsize);
bzero (buf, bufsize);
+
+ errno = 0;
valsize = readlink (XSTRING (filename)->data, buf, bufsize);
- if (valsize < bufsize) break;
- /* Buffer was not long enough */
- xfree (buf);
- bufsize *= 2;
- }
- if (valsize == -1)
- {
- xfree (buf);
- return Qnil;
+ if (valsize == -1)
+ {
+#ifdef ERANGE
+ /* HP-UX reports ERANGE if buffer is too small. */
+ if (errno == ERANGE)
+ valsize = bufsize;
+ else
+#endif
+ {
+ xfree (buf);
+ return Qnil;
+ }
+ }
}
+ while (valsize >= bufsize);
+
val = make_string (buf, valsize);
if (buf[0] == '/' && index (buf, ':'))
val = concat2 (build_string ("/:"), val);
return Qnil;
}
+
+/* Used to pass values from insert-file-contents to read_non_regular. */
+
+static int non_regular_fd;
+static int non_regular_inserted;
+static int non_regular_nbytes;
+
+
+/* Read from a non-regular file.
+ Read non_regular_trytry bytes max from non_regular_fd.
+ Non_regular_inserted specifies where to put the read bytes.
+ Value is the number of bytes read. */
+
+static Lisp_Object
+read_non_regular ()
+{
+ int nbytes;
+
+ immediate_quit = 1;
+ QUIT;
+ nbytes = emacs_read (non_regular_fd,
+ BEG_ADDR + PT_BYTE - 1 + non_regular_inserted,
+ non_regular_nbytes);
+ Fsignal (Qquit, Qnil);
+ immediate_quit = 0;
+ return make_number (nbytes);
+}
+
+
+/* Condition-case handler used when reading from non-regular files
+ in insert-file-contents. */
+
+static Lisp_Object
+read_non_regular_quit ()
+{
+ return Qnil;
+}
+
+
DEFUN ("insert-file-contents", Finsert_file_contents, Sinsert_file_contents,
1, 5, 0,
"Insert contents of file FILENAME after point.\n\
int inserted = 0;
register int how_much;
register int unprocessed;
- int count = specpdl_ptr - specpdl;
+ int count = BINDING_STACK_SIZE ();
struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
Lisp_Object handler, val, insval, orig_filename;
Lisp_Object p;
int replace_handled = 0;
int set_coding_system = 0;
int coding_system_decided = 0;
+ int gap_size;
+ int read_quit = 0;
if (current_buffer->base_buffer && ! NILP (visit))
error ("Cannot do file visiting in an indirect buffer");
if (XINT (end) != st.st_size
|| ((int) st.st_size * 4) / 4 != st.st_size)
error ("Maximum buffer size exceeded");
+
+ /* The file size returned from stat may be zero, but data
+ may be readable nonetheless, for example when this is a
+ file in the /proc filesystem. */
+ if (st.st_size == 0)
+ XSETINT (end, READ_BUF_SIZE);
}
}
report_file_error ("Setting file position",
Fcons (orig_filename, Qnil));
- total_read = 0;
+ total_read = nread = 0;
while (total_read < trial)
{
nread = emacs_read (fd, buffer + total_read, trial - total_read);
- if (nread <= 0)
+ if (nread < 0)
error ("IO error reading %s: %s",
XSTRING (orig_filename)->data, emacs_strerror (errno));
+ else if (nread == 0)
+ break;
total_read += nread;
}
+
/* Scan this bufferful from the end, comparing with
the Emacs buffer. */
bufpos = total_read;
+
/* Compare with same_at_start to avoid counting some buffer text
as matching both at the file's beginning and at the end. */
while (bufpos > 0 && same_at_end > same_at_start
giveup_match_end = 1;
break;
}
+
+ if (nread == 0)
+ break;
}
immediate_quit = 0;
/* Convert this batch with results in CONVERSION_BUFFER. */
if (how_much >= total) /* This is the last block. */
coding.mode |= CODING_MODE_LAST_BLOCK;
+ if (coding.composing != COMPOSITION_DISABLED)
+ coding_allocate_composition_data (&coding, BEGV);
result = decode_coding (&coding, read_buf,
conversion_buffer + inserted,
this, bufsize - inserted);
SET_PT_BOTH (temp, same_at_start);
insert_1 (conversion_buffer + same_at_start - BEG_BYTE, inserted,
0, 0, 0);
+ if (coding.cmp_data && coding.cmp_data->used)
+ coding_restore_composition (&coding, Fcurrent_buffer ());
+ coding_free_composition_data (&coding);
+
/* Set `inserted' to the number of inserted characters. */
inserted = PT - temp;
before exiting the loop, it is set to a negative value if I/O
error occurs. */
how_much = 0;
+
/* Total bytes inserted. */
inserted = 0;
+
/* Here, we don't do code conversion in the loop. It is done by
code_convert_region after all data are read into the buffer. */
- while (how_much < total)
- {
+ {
+ int 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);
- int this;
+ int trytry = min (total - how_much, READ_BUF_SIZE);
+ int this;
- /* For a special file, GAP_SIZE should be checked every time. */
- if (not_regular && GAP_SIZE < trytry)
- make_gap (total - GAP_SIZE);
+ if (not_regular)
+ {
+ Lisp_Object val;
- /* Allow quitting out of the actual I/O. */
- immediate_quit = 1;
- QUIT;
- this = emacs_read (fd, BYTE_POS_ADDR (PT_BYTE + inserted - 1) + 1,
- trytry);
- immediate_quit = 0;
+ /* Maybe make more room. */
+ if (gap_size < trytry)
+ {
+ make_gap (total - gap_size);
+ gap_size = GAP_SIZE;
+ }
- if (this <= 0)
- {
- how_much = this;
- break;
- }
+ /* 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;
+ val = internal_condition_case_1 (read_non_regular, Qnil, Qerror,
+ read_non_regular_quit);
+ if (NILP (val))
+ {
+ read_quit = 1;
+ break;
+ }
- GAP_SIZE -= this;
- GPT_BYTE += this;
- ZV_BYTE += this;
- Z_BYTE += this;
- GPT += this;
- ZV += this;
- Z += this;
-
- /* 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;
- }
+ this = XINT (val);
+ }
+ 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, BEG_ADDR + PT_BYTE - 1 + inserted, trytry);
+ immediate_quit = 0;
+ }
+
+ if (this <= 0)
+ {
+ how_much = this;
+ break;
+ }
+
+ gap_size -= this;
+
+ /* 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;
+ }
+ }
+
+ /* 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. */
}
if (!NILP (visit)
+ /* Can't do this if part of the buffer might be preserved. */
+ && NILP (replace)
&& (coding.type == coding_type_no_conversion
|| coding.type == coding_type_raw_text))
{
- /* Visiting a file with these coding system always make the buffer
- unibyte. */
+ /* Visiting a file with these coding system makes the buffer
+ unibyte. */
current_buffer->enable_multibyte_characters = Qnil;
coding.dst_multibyte = 0;
}
/* Decode file format */
if (inserted > 0)
{
+ int empty_undo_list_p = 0;
+
+ /* If we're anyway going to discard undo information, don't
+ record it in the first place. The buffer's undo list at this
+ point is either nil or t when visiting a file. */
+ if (!NILP (visit))
+ {
+ empty_undo_list_p = NILP (current_buffer->undo_list);
+ current_buffer->undo_list = Qt;
+ }
+
insval = call3 (Qformat_decode,
Qnil, make_number (inserted), visit);
CHECK_NUMBER (insval, 0);
inserted = XFASTINT (insval);
+
+ if (!NILP (visit))
+ current_buffer->undo_list = empty_undo_list_p ? Qnil : Qt;
}
if (set_coding_system)
report_file_error ("Opening input file", Fcons (orig_filename, Qnil));
}
+ if (read_quit)
+ Fsignal (Qquit, Qnil);
+
/* ??? Retval needs to be dealt with in all cases consistently. */
if (NILP (val))
val = Fcons (orig_filename,
Lisp_Object visit_file;
Lisp_Object annotations;
Lisp_Object encoded_filename;
- int visiting, quietly;
+ int visiting = (EQ (visit, Qt) || STRINGP (visit));
+ int quietly = !NILP (visit);
struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
struct buffer *given_buffer;
#ifdef DOS_NT
#endif /* DOS_NT */
struct coding_system coding;
- if (current_buffer->base_buffer && ! NILP (visit))
+ if (current_buffer->base_buffer && visiting)
error ("Cannot do file visiting in an indirect buffer");
if (!NILP (start) && !STRINGP (start))
{
/* If the variable `buffer-file-coding-system' is set locally,
it means that the file was read with some kind of code
- conversion or the varialbe is explicitely set by users. We
+ conversion or the variable is explicitly set by users. We
had better write it out with the same coding system even if
`enable-multibyte-characters' is nil.
visit_file = filename;
UNGCPRO;
- visiting = (EQ (visit, Qt) || STRINGP (visit));
- quietly = !NILP (visit);
-
annotations = Qnil;
if (NILP (lockname))
#else /* not VMS */
#ifdef DOS_NT
desc = emacs_open (fn,
- O_WRONLY | O_TRUNC | O_CREAT | buffer_file_type
- | (mustbenew == Qexcl ? O_EXCL : 0),
+ O_WRONLY | O_CREAT | buffer_file_type
+ | (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
register int nbytes;
char buf[WRITE_BUF_SIZE];
int return_val = 0;
- int require_encoding_p;
if (start >= end)
coding->composing = COMPOSITION_DISABLED;
}
\f
Lisp_Object
-auto_save_error ()
+auto_save_error (error)
+ Lisp_Object error;
{
+ Lisp_Object args[3], msg;
+ int i, nbytes;
+ struct gcpro gcpro1;
+
ring_bell ();
- message_with_string ("Autosaving...error for %s", current_buffer->name, 1);
- Fsleep_for (make_number (1), Qnil);
- message_with_string ("Autosaving...error for %s", current_buffer->name, 0);
- Fsleep_for (make_number (1), Qnil);
- message_with_string ("Autosaving...error for %s", current_buffer->name, 0);
- Fsleep_for (make_number (1), Qnil);
+
+ args[0] = build_string ("Auto-saving %s: %s");
+ args[1] = current_buffer->name;
+ args[2] = Ferror_message_string (error);
+ msg = Fformat (3, args);
+ GCPRO1 (msg);
+ nbytes = STRING_BYTES (XSTRING (msg));
+
+ for (i = 0; i < 3; ++i)
+ {
+ if (i == 0)
+ message2 (XSTRING (msg)->data, nbytes, STRING_MULTIBYTE (msg));
+ else
+ message2_nolog (XSTRING (msg)->data, nbytes, STRING_MULTIBYTE (msg));
+ Fsleep_for (make_number (1), Qnil);
+ }
+
+ UNGCPRO;
return Qnil;
}
if (!NILP (stream))
fclose ((FILE *) (XFASTINT (XCAR (stream)) << 16
| XFASTINT (XCDR (stream))));
+ pop_message ();
return Qnil;
}
if (STRINGP (Vauto_save_list_file_name))
{
- Lisp_Object listfile, dir;
+ Lisp_Object listfile;
listfile = Fexpand_file_name (Vauto_save_list_file_name, Qnil);
-
- dir = Ffile_name_directory (listfile);
- if (NILP (Ffile_directory_p (dir)))
- call2 (Qmake_directory, dir, Qt);
+
+ /* Don't try to create the directory when shutting down Emacs,
+ because creating the directory might signal an error, and
+ that would leave Emacs in a strange state. */
+ if (!NILP (Vrun_hooks))
+ {
+ Lisp_Object dir;
+ dir = Ffile_name_directory (listfile);
+ if (NILP (Ffile_directory_p (dir)))
+ call2 (Qmake_directory, dir, Qt);
+ }
stream = fopen (XSTRING (listfile)->data, "w");
if (stream != NULL)
Vquit_flag = oquit;
- pop_message ();
unbind_to (count, Qnil);
return Qnil;
}
/* If dir starts with user's homedir, change that to ~. */
homedir = (char *) egetenv ("HOME");
#ifdef DOS_NT
- homedir = strcpy (alloca (strlen (homedir) + 1), homedir);
- CORRECT_DIR_SEPS (homedir);
+ /* homedir can be NULL in temacs, since Vprocess_environment is not
+ yet set up. We shouldn't crash in that case. */
+ if (homedir != 0)
+ {
+ homedir = strcpy (alloca (strlen (homedir) + 1), homedir);
+ CORRECT_DIR_SEPS (homedir);
+ }
#endif
if (homedir != 0
&& STRINGP (dir)
The value should be either ?/ or ?\\ (any other value is treated as ?\\).\n\
This variable affects the built-in functions only on Windows,\n\
on other platforms, it is initialized so that Lisp code can find out\n\
-what the normal separator is.");
+what the normal separator is.\n\
+\n\
+WARNING: This variable is deprecated and will be removed in the near\n\
+future. DO NOT USE IT.");
DEFVAR_LISP ("file-name-handler-alist", &Vfile_name_handler_alist,
"*Alist of elements (REGEXP . HANDLER) for file names handled specially.\n\