]> code.delx.au - gnu-emacs/blobdiff - src/fileio.c
(MINI_WINDOW_P): Use NILP.
[gnu-emacs] / src / fileio.c
index a07ea27b1de34dae2b12c9033408affc983419f0..b152f0a74e086515cf29baa7c66fd108ede571e3 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
@@ -1127,9 +1127,9 @@ See also the function `substitute-in-file-name'.")
     }
 #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
@@ -1165,6 +1165,13 @@ See also the function `substitute-in-file-name'.")
                  || (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;
@@ -1525,7 +1532,8 @@ See also the function `substitute-in-file-name'.")
 
   /* 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;
@@ -1601,6 +1609,14 @@ See also the function `substitute-in-file-name'.")
            ++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++;
@@ -2278,8 +2294,8 @@ This is what happens in interactive use with M-x.\n\
 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];
@@ -2306,7 +2322,7 @@ A prefix arg makes KEEP-TIME non-nil.")
     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);
@@ -2318,6 +2334,22 @@ A prefix arg makes KEEP-TIME non-nil.")
   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));
@@ -2381,7 +2413,7 @@ A prefix arg makes KEEP-TIME non-nil.")
 
   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);
@@ -2407,6 +2439,7 @@ A prefix arg makes KEEP-TIME non-nil.")
     }
 
   emacs_close (ifd);
+#endif /* WINDOWSNT */
 
   /* Discard the unwind protects.  */
   specpdl_ptr = specpdl + count;
@@ -2934,12 +2967,13 @@ See also `file-exists-p' and `file-attributes'.")
 
   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.
@@ -2956,7 +2990,7 @@ See also `file-exists-p' and `file-attributes'.")
     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
@@ -3062,22 +3096,32 @@ Otherwise returns nil.")
 
   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);
@@ -3385,6 +3429,45 @@ decide_coding_unwind (unwind_data)
   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\
@@ -3418,7 +3501,7 @@ actually used.")
   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;
@@ -3430,6 +3513,8 @@ actually used.")
   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");
@@ -3556,6 +3641,12 @@ actually used.")
          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);
        }
     }
 
@@ -3779,18 +3870,22 @@ actually used.")
            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
@@ -3810,6 +3905,9 @@ actually used.")
                giveup_match_end = 1;
              break;
            }
+
+         if (nread == 0)
+           break;
        }
       immediate_quit = 0;
 
@@ -3931,6 +4029,8 @@ actually used.")
              /* 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);
@@ -4039,6 +4139,10 @@ actually used.")
       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;
 
@@ -4083,50 +4187,86 @@ actually used.")
      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.  */
@@ -4219,11 +4359,13 @@ actually used.")
     }
 
   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;
     }
@@ -4289,10 +4431,24 @@ actually used.")
   /* 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)
@@ -4328,6 +4484,9 @@ actually used.")
       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,
@@ -4411,7 +4570,8 @@ This does code conversion according to the value of\n\
   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
@@ -4419,7 +4579,7 @@ This does code conversion according to the value of\n\
 #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))
@@ -4439,7 +4599,7 @@ This does code conversion according to the value of\n\
       {
        /* 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.
 
@@ -4531,9 +4691,6 @@ This does code conversion according to the value of\n\
     visit_file = filename;
   UNGCPRO;
 
-  visiting = (EQ (visit, Qt) || STRINGP (visit));
-  quietly = !NILP (visit);
-
   annotations = Qnil;
 
   if (NILP (lockname))
@@ -4656,8 +4813,8 @@ This does code conversion according to the value of\n\
 #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
@@ -5038,7 +5195,6 @@ e_write (desc, string, start, end, coding)
   register int nbytes;
   char buf[WRITE_BUF_SIZE];
   int return_val = 0;
-  int require_encoding_p;
 
   if (start >= end)
     coding->composing = COMPOSITION_DISABLED;
@@ -5212,15 +5368,32 @@ An argument specifies the modification time value to use\n\
 }
 \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;
 }
 
@@ -5251,6 +5424,7 @@ do_auto_save_unwind (stream)  /* used as unwind-protect function */
   if (!NILP (stream))
     fclose ((FILE *) (XFASTINT (XCAR (stream)) << 16
                      | XFASTINT (XCDR (stream))));
+  pop_message ();
   return Qnil;
 }
 
@@ -5302,13 +5476,20 @@ A non-nil CURRENT-ONLY argument means save only current buffer.")
 
   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)
@@ -5446,7 +5627,6 @@ A non-nil CURRENT-ONLY argument means save only current buffer.")
 
   Vquit_flag = oquit;
 
-  pop_message ();
   unbind_to (count, Qnil);
   return Qnil;
 }
@@ -5629,8 +5809,13 @@ provides a file dialog box..")
   /* 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)
@@ -5923,7 +6108,10 @@ nil means use format `var'.  This variable is meaningful only on VMS.");
 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\