Subject: Re: bug#5303: 23.1.91; Cannot load .emacs-history from savehist.el

From: Eli Zaretskii <[email-masked]>
Date: Tue, 19 Jan 2010 23:24:55 +0200

> From: Michael Albinus <[email-masked]>
> Cc: Drew Adams <[email-masked]>,  [email-masked],  [email-masked],  [email-masked]
> Date: Tue, 19 Jan 2010 20:36:27 +0100
> 
> Eli Zaretskii <[email-masked]> writes:
> 
> > What is in the-file.el? will an empty file do? if not, what should be
> > there to reproduce the problem?
> 
> ------
> (setq command-history '((describe-key "^Z")))
> ------

Thanks.

I see the problem, but there's too many dragons here, or maybe it's
too late.

Here's the story I got so far.

The root cause for the problem is that we read this file in text mode,
not in binary mode.  Because of that, the literal C-z character in the
file is treated as EOF (a CP/M legacy, no less), and the rest is
history.

Why do we read the file in text mode?  That's where things become
rather murky.  AFAIK, we ought to be reading the file with
load-with-code-conversion, which is the value of
Vload_source_file_function:

      /* We are loading a source file (*.el).  */
      if (!NILP (Vload_source_file_function))
	{
	  Lisp_Object val;

	  if (fd >= 0)
	    emacs_close (fd);
	  val = call4 (Vload_source_file_function, found, hist_file_name,
		       NILP (noerror) ? Qnil : Qt,
		       (NILP (nomessage) || force_load_messages) ? Qnil : Qt);
	  return unbind_to (count, val);
	}

But we don't get to this code because we are caught here:

  if (!bcmp (SDATA (found) + SBYTES (found) - 4,
	     ".elc", 4)
      || (version = safe_to_load_p (fd)) > 0)
    /* Load .elc files directly, but not when they are
       remote and have no handler!  */

[Btw, I don't understand this condition.  What it's supposed to do?
allow us to load a byte-compiled file whose name does not end in a
.elc?]

(Are you saying "Huh??" yet?)  And we are caught here because,
although the file name does not end in .elc, safe_to_load_p returns
1.  Why? because fd is -2, and safe_to_load_p bravely returns 1 when
it fails to read from the file:

  static int
  safe_to_load_p (fd)
       int fd;
  {
    char buf[512];
    int nbytes, i;
    int safe_p = 1;
    int version = 1;

    /* Read the first few bytes from the file, and look for a line
       specifying the byte compiler version used.  */
    nbytes = emacs_read (fd, buf, sizeof buf - 1);
    if (nbytes > 0)
      {
      ...
      }
    if (safe_p)
      safe_p = version;

    lseek (fd, 0, SEEK_SET);
    return safe_p;
  }

Granted, trying to read from fd = -2 fails, and we return 1 here.  Is
that really supposed to happen?

Another question is why fd is -2.  That means `openp' detected that
C:/the-file.el is a ``magic'' file, i.e. it has a file handler.  But
this happens in a recursive call to `load', the one where, as Michael
says:

> the last action I can see is disabling Tramp' file name completion
> handler, and calling `load', again.

It looks like disabling Tramp's file name completion handler does not
prevent `openp' from thinking that C:/the-file.el has a handler.
However, when we actually try to find this handler after `openp'
returns -2, the handler turns out to be nil:

  /* If FD is -2, that means openp found a magic file.  */
  if (fd == -2)
    {
      if (NILP (Fequal (found, file)))
	/* If FOUND is a different file name from FILE,
	   find its handler even if we have already inhibited
	   the `load' operation on FILE.  */
	handler = Ffind_file_name_handler (found, Qt);
      else
	handler = Ffind_file_name_handler (found, Qload);
      if (! NILP (handler))
	return call5 (handler, Qload, found, noerror, nomessage, Qt);
    }

Thus, we don't call the handler, and end up with fd = -2, but in the
portion of code that doesn't seem to be able to handle this situation
well: it just calls readevalloop.  What is this part of Fload for? is
it for the case where we don't yet have mule.el loaded, and thus
load-with-code-conversion is not available?

I didn't yet have time to see why Emacs 23.1 does not hit this
problem, but given the above mess, it could well be by sheer luck.

Anyway, it's too late now.  If no one beats me to it, I will continue
tomorrow.