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.
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 stream UNINIT;
lread_fd fd;
#ifdef USE_ANDROID_ASSETS
int rc;
void *asset;
#endif
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)
{
#if !defined USE_ANDROID_ASSETS
fd = -1;
#else
fd.asset = NULL;
fd.fd = -1;
#endif
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);
}
#if !defined USE_ANDROID_ASSETS
fd = openp (Vload_path, file, suffixes, &found, Qnil,
load_prefer_newer, no_native, NULL);
#else
asset = NULL;
rc = openp (Vload_path, file, suffixes, &found, Qnil,
load_prefer_newer, no_native, &asset);
fd.fd = rc;
fd.asset = asset;
/* fd.asset will be non-NULL if this is actually an asset
file. */
#endif
}
if (lread_fd_cmp (-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 (lread_fd_cmp (-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 !defined USE_ANDROID_ASSETS
if (0 <= fd)
{
fd_index = SPECPDL_INDEX ();
record_unwind_protect_int (close_file_unwind, fd);
}
#else
if (fd.asset || fd.fd >= 0)
{
/* Use a different kind of unwind_protect here. */
fd_index = SPECPDL_INDEX ();
record_unwind_protect_ptr (close_file_unwind_android_fd,
&fd);
}
#endif
#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. */
|| (lread_fd_p
&& (version = safe_to_load_version (file, fd)) > 1))
/* Load .elc files directly, but not when they are
remote and have no handler! */
{
if (!lread_fd_cmp (-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 (lread_fd_p)
{
lread_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 (!lread_fd_p)
{
/* 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 = file_stream_invalid;
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
#if !defined USE_ANDROID_ASSETS
stream = emacs_fdopen (fd, fmode);
#else
/* Android systems use special file descriptors which can point
into compressed data and double as file streams. FMODE is
unused. */
((void) fmode);
stream = fd;
#endif
#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 (lread_fd_p)
{
lread_close (fd);
clear_unwind_protect (fd_index);
}
}
else
{
if (!file_stream_valid_p (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_lexical_cookie (Qget_file_char) == Cookie_Lex)
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;
}
/* The "...done" messages are shown only in interactive mode, because
the echo-area can display only the last message, and we want to
avoid the impression that the load is still in progress. */
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;
}