Function: load

load is a function defined in lread.c.

Signature

(load FILE &optional NOERROR NOMESSAGE NOSUFFIX MUST-SUFFIX)

Documentation

Execute a file of Lisp code named FILE.

First try FILE with .elc appended, then try with .el, then try with a system-dependent suffix of dynamic modules (see load-suffixes), then try FILE unmodified (the exact suffixes in the exact order are determined by load-suffixes). Environment variable references in FILE are replaced with their values by calling substitute-in-file-name. This function searches the directories in load-path.

If optional second arg NOERROR is non-nil, report no error if FILE doesn't exist. Print messages at start and end of loading unless optional third arg NOMESSAGE is non-nil (but force-load-messages overrides that). If optional fourth arg NOSUFFIX is non-nil, don't try adding suffixes to the specified name FILE. If optional fifth arg MUST-SUFFIX is non-nil, insist on the suffix .elc or .el or the module suffix; don't accept just FILE unless it ends in one of those suffixes or includes a directory name.

If NOSUFFIX is nil, then if a file could not be found, try looking for a different representation of the file by adding non-empty suffixes to its name, before trying another file. Emacs uses this feature to find compressed versions of files when Auto Compression mode is enabled. If NOSUFFIX is non-nil, disable this feature.

The suffixes that this function tries out, when NOSUFFIX is nil, are given by the return value of get-load-suffixes and the values listed in load-file-rep-suffixes. If MUST-SUFFIX is non-nil, only the return value of get-load-suffixes is used, i.e. the file name is required to have a non-empty suffix.

When searching suffixes, this function normally stops at the first one that exists. If the option load-prefer-newer is non-nil, however, it tries all suffixes, and uses whichever file is the newest.

Loading a file records its definitions, and its provide and require calls, in an element of load-history whose car is the file name loaded. See load-history.

While the file is in the process of being loaded, the variable load-in-progress is non-nil and the variable load-file-name is bound to the file's name.

Return t if the file exists and loads successfully.

View in manual

Probably introduced at or before Emacs version 1.1.

Source Code

// Defined in /usr/src/emacs/src/lread.c
// Skipping highlighting due to helpful-max-highlight.
{
  FILE *stream UNINIT;
  int fd;
  specpdl_ref fd_index UNINIT;
  specpdl_ref count = SPECPDL_INDEX ();
  Lisp_Object found, efound, hist_file_name;
  /* True means we printed the ".el is newer" message.  */
  bool newer = 0;
  /* True means we are loading a compiled file.  */
  bool compiled = 0;
  Lisp_Object handler;
  const char *fmode = "r" FOPEN_TEXT;
  int version;

  CHECK_STRING (file);

  /* If file name is magic, call the handler.  */
  handler = Ffind_file_name_handler (file, Qload);
  if (!NILP (handler))
    return
      call6 (handler, Qload, file, noerror, nomessage, nosuffix, must_suffix);

  /* The presence of this call is the result of a historical accident:
     it used to be in every file-operation and when it got removed
     everywhere, it accidentally stayed here.  Since then, enough people
     supposedly have things like (load "$PROJECT/foo.el") in their .emacs
     that it seemed risky to remove.  */
  if (! NILP (noerror))
    {
      file = internal_condition_case_1 (Fsubstitute_in_file_name, file,
					Qt, load_error_handler);
      if (NILP (file))
	return Qnil;
    }
  else
    file = Fsubstitute_in_file_name (file);

  bool no_native = suffix_p (file, ".elc");

  /* Avoid weird lossage with null string as arg,
     since it would try to load a directory as a Lisp file.  */
  if (SCHARS (file) == 0)
    {
      fd = -1;
      errno = ENOENT;
    }
  else
    {
      Lisp_Object suffixes;
      found = Qnil;

      if (! NILP (must_suffix))
	{
	  /* Don't insist on adding a suffix if FILE already ends with one.  */
	  if (suffix_p (file, ".el")
	      || suffix_p (file, ".elc")
#ifdef HAVE_MODULES
	      || suffix_p (file, MODULES_SUFFIX)
#ifdef MODULES_SECONDARY_SUFFIX
              || suffix_p (file, MODULES_SECONDARY_SUFFIX)
#endif
#endif
#ifdef HAVE_NATIVE_COMP
              || suffix_p (file, NATIVE_ELISP_SUFFIX)
#endif
	      )
	    must_suffix = Qnil;
	  /* Don't insist on adding a suffix
	     if the argument includes a directory name.  */
	  else if (! NILP (Ffile_name_directory (file)))
	    must_suffix = Qnil;
	}

      if (!NILP (nosuffix))
	suffixes = Qnil;
      else
	{
	  suffixes = Fget_load_suffixes ();
	  if (NILP (must_suffix))
	    suffixes = CALLN (Fappend, suffixes, Vload_file_rep_suffixes);
	}

      fd =
	openp (Vload_path, file, suffixes, &found, Qnil, load_prefer_newer,
	       no_native);
    }

  if (fd == -1)
    {
      if (NILP (noerror))
	report_file_error ("Cannot open load file", file);
      return Qnil;
    }

  /* Tell startup.el whether or not we found the user's init file.  */
  if (EQ (Qt, Vuser_init_file))
    Vuser_init_file = found;

  /* 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);
#ifdef DOS_NT
      /* Tramp has to deal with semi-broken packages that prepend
	 drive letters to remote files.  For that reason, Tramp
	 catches file operations that test for file existence, which
	 makes openp think X:/foo.elc files are remote.  However,
	 Tramp does not catch `load' operations for such files, so we
	 end up with a nil as the `load' handler above.  If we would
	 continue with fd = -2, we will behave wrongly, and in
	 particular try reading a .elc file in the "rt" mode instead
	 of "rb".  See bug #9311 for the results.  To work around
	 this, we try to open the file locally, and go with that if it
	 succeeds.  */
      fd = emacs_open (SSDATA (ENCODE_FILE (found)), O_RDONLY, 0);
      if (fd == -1)
	fd = -2;
#endif
    }

  if (0 <= fd)
    {
      fd_index = SPECPDL_INDEX ();
      record_unwind_protect_int (close_file_unwind, fd);
    }

#ifdef HAVE_MODULES
  bool is_module =
    suffix_p (found, MODULES_SUFFIX)
#ifdef MODULES_SECONDARY_SUFFIX
    || suffix_p (found, MODULES_SECONDARY_SUFFIX)
#endif
    ;
#else
  bool is_module = false;
#endif

#ifdef HAVE_NATIVE_COMP
  bool is_native_elisp = suffix_p (found, NATIVE_ELISP_SUFFIX);
#else
  bool is_native_elisp = false;
#endif

  /* Check if we're stuck in a recursive load cycle.

     2000-09-21: It's not possible to just check for the file loaded
     being a member of Vloads_in_progress.  This fails because of the
     way the byte compiler currently works; `provide's are not
     evaluated, see font-lock.el/jit-lock.el as an example.  This
     leads to a certain amount of ``normal'' recursion.

     Also, just loading a file recursively is not always an error in
     the general case; the second load may do something different.  */
  {
    int load_count = 0;
    Lisp_Object tem = Vloads_in_progress;
    FOR_EACH_TAIL_SAFE (tem)
      if (!NILP (Fequal (found, XCAR (tem))) && (++load_count > 3))
	signal_error ("Recursive load", Fcons (found, Vloads_in_progress));
    record_unwind_protect (record_load_unwind, Vloads_in_progress);
    Vloads_in_progress = Fcons (found, Vloads_in_progress);
  }

  /* All loads are by default dynamic, unless the file itself specifies
     otherwise using a file-variable in the first line.  This is bound here
     so that it takes effect whether or not we use
     Vload_source_file_function.  */
  specbind (Qlexical_binding, Qnil);

  Lisp_Object found_eff =
    is_native_elisp
    ? compute_found_effective (found)
    : found;

  hist_file_name = (! NILP (Vpurify_flag)
                    ? concat2 (Ffile_name_directory (file),
                               Ffile_name_nondirectory (found_eff))
                    : found_eff);

  version = -1;

  /* Check for the presence of unescaped character literals and warn
     about them. */
  specbind (Qlread_unescaped_character_literals, Qnil);
  record_unwind_protect (load_warn_unescaped_character_literals, file);

  bool is_elc = suffix_p (found, ".elc");
  if (is_elc
      /* version = 1 means the file is empty, in which case we can
	 treat it as not byte-compiled.  */
      || (fd >= 0 && (version = safe_to_load_version (file, fd)) > 1))
    /* Load .elc files directly, but not when they are
       remote and have no handler!  */
    {
      if (fd != -2)
	{
	  struct stat s1, s2;
	  int result;

	  struct timespec epoch_timespec = {(time_t)0, 0}; /* 1970-01-01T00:00 UTC */
	  if (version < 0 && !(version = safe_to_load_version (file, fd)))
	    error ("File `%s' was not compiled in Emacs", SDATA (found));

	  compiled = 1;

	  efound = ENCODE_FILE (found);
	  fmode = "r" FOPEN_BINARY;

          /* openp already checked for newness, no point doing it again.
             FIXME would be nice to get a message when openp
             ignores suffix order due to load_prefer_newer.  */
          if (!load_prefer_newer && is_elc)
            {
	      result = emacs_fstatat (AT_FDCWD, SSDATA (efound), &s1, 0);
              if (result == 0)
                {
                  SSET (efound, SBYTES (efound) - 1, 0);
		  result = emacs_fstatat (AT_FDCWD, SSDATA (efound), &s2, 0);
                  SSET (efound, SBYTES (efound) - 1, 'c');
                }

              if (result == 0
                  && timespec_cmp (get_stat_mtime (&s1), get_stat_mtime (&s2)) < 0)
                {
                  /* Make the progress messages mention that source is newer.  */
                  newer = 1;

                  /* If we won't print another message, mention this anyway.  */
                  if (!NILP (nomessage) && !force_load_messages
		      /* We don't want this message during
			 bootstrapping for the "compile-first" .elc
			 files, which have had their timestamps set to
			 the epoch.  See bug #58224.  */
		      && timespec_cmp (get_stat_mtime (&s1), epoch_timespec))
                    {
                      Lisp_Object msg_file;
                      msg_file = Fsubstring (found, make_fixnum (0), make_fixnum (-1));
                      message_with_string ("Source file `%s' newer than byte-compiled file; using older file",
                                           msg_file, 1);
                    }
                }
            } /* !load_prefer_newer */
	}
    }
  else if (!is_module && !is_native_elisp)
    {
      /* We are loading a source file (*.el).  */
      if (!NILP (Vload_source_file_function))
	{
	  Lisp_Object val;

	  if (fd >= 0)
	    {
	      emacs_close (fd);
	      clear_unwind_protect (fd_index);
	    }
	  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);
	}
    }

  if (fd < 0)
    {
      /* We somehow got here with fd == -2, meaning the file is deemed
	 to be remote.  Don't even try to reopen the file locally;
	 just force a failure.  */
      stream = NULL;
      errno = EINVAL;
    }
  else if (!is_module && !is_native_elisp)
    {
#ifdef WINDOWSNT
      emacs_close (fd);
      clear_unwind_protect (fd_index);
      efound = ENCODE_FILE (found);
      stream = emacs_fopen (SSDATA (efound), fmode);
#else
      stream = fdopen (fd, fmode);
#endif
    }

  /* Declare here rather than inside the else-part because the storage
     might be accessed by the unbind_to call below.  */
  struct infile input;

  if (is_module || is_native_elisp)
    {
      /* `module-load' uses the file name, so we can close the stream
         now.  */
      if (fd >= 0)
        {
          emacs_close (fd);
          clear_unwind_protect (fd_index);
        }
    }
  else
    {
      if (! stream)
        report_file_error ("Opening stdio stream", file);
      set_unwind_protect_ptr (fd_index, close_infile_unwind, infile);
      input.stream = stream;
      input.lookahead = 0;
      infile = &input;
      unread_char = -1;
    }

  if (! NILP (Vpurify_flag))
    Vpreloaded_file_list = Fcons (Fpurecopy (file), Vpreloaded_file_list);

  if (NILP (nomessage) || force_load_messages)
    {
      if (is_module)
        message_with_string ("Loading %s (module)...", file, 1);
      else if (is_native_elisp)
        message_with_string ("Loading %s (native compiled elisp)...", file, 1);
      else if (!compiled)
	message_with_string ("Loading %s (source)...", file, 1);
      else if (newer)
	message_with_string ("Loading %s (compiled; note, source file is newer)...",
		 file, 1);
      else /* The typical case; compiled file newer than source file.  */
	message_with_string ("Loading %s...", file, 1);
    }

  specbind (Qload_file_name, hist_file_name);
  specbind (Qload_true_file_name, found);
  specbind (Qinhibit_file_name_operation, Qnil);
  specbind (Qload_in_progress, Qt);

  if (is_module)
    {
#ifdef HAVE_MODULES
      loadhist_initialize (found);
      Fmodule_load (found);
      build_load_history (found, true);
#else
      /* This cannot happen.  */
      emacs_abort ();
#endif
    }
  else if (is_native_elisp)
    {
#ifdef HAVE_NATIVE_COMP
      loadhist_initialize (hist_file_name);
      Fnative_elisp_load (found, Qnil);
      build_load_history (hist_file_name, true);
#else
      /* This cannot happen.  */
      emacs_abort ();
#endif

    }
  else
    {
      if (lisp_file_lexically_bound_p (Qget_file_char))
        Fset (Qlexical_binding, Qt);

      if (! version || version >= 22)
        readevalloop (Qget_file_char, &input, hist_file_name,
                      0, Qnil, Qnil, Qnil, Qnil);
      else
        {
          /* We can't handle a file which was compiled with
             byte-compile-dynamic by older version of Emacs.  */
          specbind (Qload_force_doc_strings, Qt);
          readevalloop (Qget_emacs_mule_file_char, &input, hist_file_name,
                        0, Qnil, Qnil, Qnil, Qnil);
        }
    }
  unbind_to (count, Qnil);

  /* Run any eval-after-load forms for this file.  */
  if (!NILP (Ffboundp (Qdo_after_load_evaluation)))
    call1 (Qdo_after_load_evaluation, hist_file_name) ;

  for (int i = 0; i < ARRAYELTS (saved_strings); i++)
    {
      xfree (saved_strings[i].string);
      saved_strings[i].string = NULL;
      saved_strings[i].size = 0;
    }

  if (!noninteractive && (NILP (nomessage) || force_load_messages))
    {
      if (is_module)
        message_with_string ("Loading %s (module)...done", file, 1);
      else if (is_native_elisp)
	message_with_string ("Loading %s (native compiled elisp)...done", file, 1);
      else if (!compiled)
	message_with_string ("Loading %s (source)...done", file, 1);
      else if (newer)
	message_with_string ("Loading %s (compiled; note, source file is newer)...done",
		 file, 1);
      else /* The typical case; compiled file newer than source file.  */
	message_with_string ("Loading %s...done", file, 1);
    }

  return Qt;
}