Add '--init-command', '-C' to the command line switches.
In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.
The documentation has been updated correspondingly.
The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.
I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.
A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
2017-06-29 22:21:00 +08:00
|
|
|
//
|
2016-05-01 09:37:19 +08:00
|
|
|
// The main loop of the fish program.
|
2005-09-20 21:26:39 +08:00
|
|
|
/*
|
2008-01-13 03:18:48 +08:00
|
|
|
Copyright (C) 2005-2008 Axel Liljencrantz
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2006-11-01 22:47:47 +08:00
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License version 2 as
|
|
|
|
published by the Free Software Foundation.
|
2005-09-20 21:26:39 +08:00
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
2013-12-14 04:51:52 +08:00
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
2005-09-20 21:26:39 +08:00
|
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2016-03-04 07:36:17 +08:00
|
|
|
#include <getopt.h>
|
2015-07-25 23:14:25 +08:00
|
|
|
#include <limits.h>
|
2016-05-01 09:37:19 +08:00
|
|
|
#include <locale.h>
|
2017-02-13 12:24:22 +08:00
|
|
|
#include <stddef.h>
|
2015-07-25 23:14:25 +08:00
|
|
|
#include <stdint.h>
|
2005-09-20 21:26:39 +08:00
|
|
|
#include <stdio.h>
|
2016-05-01 09:37:19 +08:00
|
|
|
#include <stdlib.h>
|
2019-04-11 04:58:29 +08:00
|
|
|
#include <sys/resource.h>
|
2016-05-01 09:37:19 +08:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
2019-10-14 06:50:48 +08:00
|
|
|
|
2019-05-05 18:09:25 +08:00
|
|
|
#include <cstring>
|
2019-03-13 05:06:01 +08:00
|
|
|
#include <cwchar>
|
2016-04-21 14:00:54 +08:00
|
|
|
#include <memory>
|
2016-05-01 09:37:19 +08:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
2005-09-20 21:26:39 +08:00
|
|
|
|
|
|
|
#include "builtin.h"
|
2016-05-01 09:37:19 +08:00
|
|
|
#include "common.h"
|
2005-09-20 21:26:39 +08:00
|
|
|
#include "env.h"
|
2005-10-06 06:37:08 +08:00
|
|
|
#include "event.h"
|
2016-05-01 09:37:19 +08:00
|
|
|
#include "expand.h"
|
|
|
|
#include "fallback.h" // IWYU pragma: keep
|
|
|
|
#include "fish_version.h"
|
2019-04-20 15:15:51 +08:00
|
|
|
#include "flog.h"
|
2016-05-01 09:37:19 +08:00
|
|
|
#include "function.h"
|
2018-04-25 05:32:06 +08:00
|
|
|
#include "future_feature_flags.h"
|
2006-04-20 07:42:11 +08:00
|
|
|
#include "history.h"
|
2019-05-23 04:34:03 +08:00
|
|
|
#include "intern.h"
|
2016-05-01 09:37:19 +08:00
|
|
|
#include "io.h"
|
|
|
|
#include "parser.h"
|
|
|
|
#include "path.h"
|
|
|
|
#include "proc.h"
|
|
|
|
#include "reader.h"
|
2017-02-08 09:21:35 +08:00
|
|
|
#include "signal.h"
|
2016-05-01 09:37:19 +08:00
|
|
|
#include "wutil.h" // IWYU pragma: keep
|
2005-09-20 21:26:39 +08:00
|
|
|
|
Add '--init-command', '-C' to the command line switches.
In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.
The documentation has been updated correspondingly.
The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.
I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.
A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
2017-06-29 22:21:00 +08:00
|
|
|
// container to hold the options specified within the command line
|
|
|
|
class fish_cmd_opts_t {
|
2017-07-19 13:40:25 +08:00
|
|
|
public:
|
2018-04-25 05:32:06 +08:00
|
|
|
// Future feature flags values string
|
|
|
|
wcstring features;
|
2019-04-20 15:15:51 +08:00
|
|
|
// File path for debug output.
|
|
|
|
std::string debug_output;
|
Add '--init-command', '-C' to the command line switches.
In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.
The documentation has been updated correspondingly.
The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.
I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.
A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
2017-06-29 22:21:00 +08:00
|
|
|
// Commands to be executed in place of interactive shell.
|
|
|
|
std::vector<std::string> batch_cmds;
|
|
|
|
// Commands to execute after the shell's config has been read.
|
|
|
|
std::vector<std::string> postconfig_cmds;
|
2019-04-11 04:58:29 +08:00
|
|
|
/// Whether to print rusage-self stats after execution.
|
|
|
|
bool print_rusage_self{false};
|
2019-05-13 06:48:00 +08:00
|
|
|
/// Whether no-exec is set.
|
|
|
|
bool no_exec{false};
|
|
|
|
/// Whether this is a login shell.
|
|
|
|
bool is_login{false};
|
|
|
|
/// Whether this is an interactive session.
|
|
|
|
bool is_interactive_session{false};
|
Add '--init-command', '-C' to the command line switches.
In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.
The documentation has been updated correspondingly.
The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.
I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.
A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
2017-06-29 22:21:00 +08:00
|
|
|
};
|
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
/// If we are doing profiling, the filename to output to.
|
2019-11-19 10:34:50 +08:00
|
|
|
static const char *s_profiling_output_filename = nullptr;
|
2014-02-10 06:04:43 +08:00
|
|
|
|
2019-04-11 04:58:29 +08:00
|
|
|
/// \return a timeval converted to milliseconds.
|
|
|
|
long long tv_to_msec(const struct timeval &tv) {
|
2019-11-19 09:08:16 +08:00
|
|
|
long long msec = static_cast<long long>(tv.tv_sec) * 1000; // milliseconds per second
|
|
|
|
msec += tv.tv_usec / 1000; // microseconds per millisecond
|
2019-04-11 04:58:29 +08:00
|
|
|
return msec;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_rusage_self(FILE *fp) {
|
|
|
|
#ifndef HAVE_GETRUSAGE
|
|
|
|
fprintf(fp, "getrusage() not supported on this platform");
|
|
|
|
return;
|
|
|
|
#else
|
|
|
|
struct rusage rs;
|
|
|
|
if (getrusage(RUSAGE_SELF, &rs)) {
|
|
|
|
perror("getrusage");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
|
|
// Macs use bytes.
|
|
|
|
long rss_kb = rs.ru_maxrss / 1024;
|
|
|
|
#else
|
|
|
|
// Everyone else uses KB.
|
|
|
|
long rss_kb = rs.ru_maxrss;
|
|
|
|
#endif
|
|
|
|
fprintf(fp, " rusage self:\n");
|
|
|
|
fprintf(fp, " user time: %llu ms\n", tv_to_msec(rs.ru_utime));
|
|
|
|
fprintf(fp, " sys time: %llu ms\n", tv_to_msec(rs.ru_stime));
|
2019-11-24 03:53:49 +08:00
|
|
|
fprintf(fp, " total time: %llu ms\n", tv_to_msec(rs.ru_utime) + tv_to_msec(rs.ru_stime));
|
2019-04-11 04:58:29 +08:00
|
|
|
fprintf(fp, " max rss: %ld kb\n", rss_kb);
|
|
|
|
fprintf(fp, " signals: %ld\n", rs.ru_nsignals);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
static bool has_suffix(const std::string &path, const char *suffix, bool ignore_case) {
|
2019-03-13 06:07:07 +08:00
|
|
|
size_t pathlen = path.size(), suffixlen = std::strlen(suffix);
|
2016-05-01 09:37:19 +08:00
|
|
|
return pathlen >= suffixlen &&
|
2019-03-13 06:07:07 +08:00
|
|
|
!(ignore_case ? strcasecmp : std::strcmp)(path.c_str() + pathlen - suffixlen, suffix);
|
2012-07-09 06:20:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
/// Modifies the given path by calling realpath. Returns true if realpath succeeded, false
|
|
|
|
/// otherwise.
|
|
|
|
static bool get_realpath(std::string &path) {
|
2012-07-09 06:20:39 +08:00
|
|
|
char buff[PATH_MAX], *ptr;
|
2016-05-01 09:37:19 +08:00
|
|
|
if ((ptr = realpath(path.c_str(), buff))) {
|
2012-07-09 06:20:39 +08:00
|
|
|
path = ptr;
|
|
|
|
}
|
2019-11-19 10:34:50 +08:00
|
|
|
return ptr != nullptr;
|
2012-07-09 06:20:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
static struct config_paths_t determine_config_directory_paths(const char *argv0) {
|
2012-07-09 06:20:39 +08:00
|
|
|
struct config_paths_t paths;
|
|
|
|
bool done = false;
|
|
|
|
std::string exec_path = get_executable_path(argv0);
|
2016-05-01 09:37:19 +08:00
|
|
|
if (get_realpath(exec_path)) {
|
2018-11-26 06:06:32 +08:00
|
|
|
debug(2, L"exec_path: '%s', argv[0]: '%s'", exec_path.c_str(), argv0);
|
|
|
|
// TODO: we should determine program_name from argv0 somewhere in this file
|
2018-10-14 10:36:59 +08:00
|
|
|
|
|
|
|
#ifdef CMAKE_BINARY_DIR
|
|
|
|
// Detect if we're running right out of the CMAKE build directory
|
2018-11-26 06:06:32 +08:00
|
|
|
if (string_prefixes_string(CMAKE_BINARY_DIR, exec_path.c_str())) {
|
2019-05-05 18:09:25 +08:00
|
|
|
debug(2,
|
|
|
|
"Running out of build directory, using paths relative to CMAKE_SOURCE_DIR:\n %s",
|
|
|
|
CMAKE_SOURCE_DIR);
|
2018-10-14 10:36:59 +08:00
|
|
|
|
|
|
|
done = true;
|
|
|
|
paths.data = wcstring{L"" CMAKE_SOURCE_DIR} + L"/share";
|
2018-11-26 06:06:32 +08:00
|
|
|
paths.sysconf = wcstring{L"" CMAKE_SOURCE_DIR} + L"/etc";
|
|
|
|
paths.doc = wcstring{L"" CMAKE_SOURCE_DIR} + L"/user_doc/html";
|
|
|
|
paths.bin = wcstring{L"" CMAKE_BINARY_DIR};
|
2018-10-14 10:36:59 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
if (!done) {
|
2016-09-24 02:52:51 +08:00
|
|
|
// The next check is that we are in a reloctable directory tree
|
|
|
|
const char *installed_suffix = "/bin/fish";
|
|
|
|
const char *just_a_fish = "/fish";
|
2019-11-19 10:34:50 +08:00
|
|
|
const char *suffix = nullptr;
|
2016-09-24 02:52:51 +08:00
|
|
|
|
|
|
|
if (has_suffix(exec_path, installed_suffix, false)) {
|
|
|
|
suffix = installed_suffix;
|
|
|
|
} else if (has_suffix(exec_path, just_a_fish, false)) {
|
|
|
|
debug(2, L"'fish' not in a 'bin/', trying paths relative to source tree");
|
|
|
|
suffix = just_a_fish;
|
2012-07-09 06:20:39 +08:00
|
|
|
}
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2016-09-24 02:52:51 +08:00
|
|
|
if (suffix) {
|
|
|
|
bool seems_installed = (suffix == installed_suffix);
|
|
|
|
|
2012-07-09 06:20:39 +08:00
|
|
|
wcstring base_path = str2wcstring(exec_path);
|
2019-03-13 06:07:07 +08:00
|
|
|
base_path.resize(base_path.size() - std::strlen(suffix));
|
2014-04-01 01:01:39 +08:00
|
|
|
|
2016-09-24 02:52:51 +08:00
|
|
|
paths.data = base_path + (seems_installed ? L"/share/fish" : L"/share");
|
|
|
|
paths.sysconf = base_path + (seems_installed ? L"/etc/fish" : L"/etc");
|
|
|
|
paths.doc = base_path + (seems_installed ? L"/share/doc/fish" : L"/user_doc/html");
|
|
|
|
paths.bin = base_path + (seems_installed ? L"/bin" : L"");
|
2014-04-01 01:01:39 +08:00
|
|
|
|
2018-07-22 11:57:21 +08:00
|
|
|
// Check only that the data and sysconf directories exist. Handle the doc
|
|
|
|
// directories separately.
|
|
|
|
struct stat buf;
|
|
|
|
if (0 == wstat(paths.data, &buf) && 0 == wstat(paths.sysconf, &buf)) {
|
2016-05-01 09:37:19 +08:00
|
|
|
// The docs dir may not exist; in that case fall back to the compiled in path.
|
2018-07-22 11:57:21 +08:00
|
|
|
if (0 != wstat(paths.doc, &buf)) {
|
2014-01-18 06:18:45 +08:00
|
|
|
paths.doc = L"" DOCDIR;
|
|
|
|
}
|
2012-07-09 06:20:39 +08:00
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
if (!done) {
|
|
|
|
// Fall back to what got compiled in.
|
2016-09-24 02:52:51 +08:00
|
|
|
debug(2, L"Using compiled in paths:");
|
2012-07-09 06:20:39 +08:00
|
|
|
paths.data = L"" DATADIR "/fish";
|
|
|
|
paths.sysconf = L"" SYSCONFDIR "/fish";
|
2013-10-29 00:01:21 +08:00
|
|
|
paths.doc = L"" DOCDIR;
|
2013-04-16 13:22:19 +08:00
|
|
|
paths.bin = L"" BINDIR;
|
2012-07-09 06:20:39 +08:00
|
|
|
}
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2016-10-05 11:06:14 +08:00
|
|
|
debug(2,
|
|
|
|
L"determine_config_directory_paths() results:\npaths.data: %ls\npaths.sysconf: "
|
|
|
|
L"%ls\npaths.doc: %ls\npaths.bin: %ls",
|
|
|
|
paths.data.c_str(), paths.sysconf.c_str(), paths.doc.c_str(), paths.bin.c_str());
|
2012-07-09 06:20:39 +08:00
|
|
|
return paths;
|
|
|
|
}
|
|
|
|
|
2016-02-05 04:51:55 +08:00
|
|
|
// Source the file config.fish in the given directory.
|
2016-05-01 09:37:19 +08:00
|
|
|
static void source_config_in_directory(const wcstring &dir) {
|
|
|
|
// If the config.fish file doesn't exist or isn't readable silently return. Fish versions up
|
|
|
|
// thru 2.2.0 would instead try to source the file with stderr redirected to /dev/null to deal
|
|
|
|
// with that possibility.
|
2016-02-05 04:51:55 +08:00
|
|
|
//
|
2016-05-01 09:37:19 +08:00
|
|
|
// This introduces a race condition since the readability of the file can change between this
|
|
|
|
// test and the execution of the 'source' command. However, that is not a security problem in
|
|
|
|
// this context so we ignore it.
|
2016-02-05 04:51:55 +08:00
|
|
|
const wcstring config_pathname = dir + L"/config.fish";
|
2013-09-30 04:39:41 +08:00
|
|
|
const wcstring escaped_dir = escape_string(dir, ESCAPE_ALL);
|
2016-02-05 04:51:55 +08:00
|
|
|
const wcstring escaped_pathname = escaped_dir + L"/config.fish";
|
|
|
|
if (waccess(config_pathname, R_OK) != 0) {
|
2016-05-01 09:37:19 +08:00
|
|
|
debug(2, L"not sourcing %ls (not readable or does not exist)", escaped_pathname.c_str());
|
2016-02-05 04:51:55 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
debug(2, L"sourcing %ls", escaped_pathname.c_str());
|
|
|
|
|
|
|
|
const wcstring cmd = L"builtin source " + escaped_pathname;
|
2013-09-30 04:39:41 +08:00
|
|
|
parser_t &parser = parser_t::principal_parser();
|
2018-09-09 16:36:21 +08:00
|
|
|
set_is_within_fish_initialization(true);
|
2019-12-23 08:27:03 +08:00
|
|
|
parser.eval(cmd, io_chain_t());
|
2018-09-09 16:36:21 +08:00
|
|
|
set_is_within_fish_initialization(false);
|
2013-09-30 04:39:41 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
/// Parse init files. exec_path is the path of fish executable as determined by argv[0].
|
|
|
|
static int read_init(const struct config_paths_t &paths) {
|
2013-09-30 04:39:41 +08:00
|
|
|
source_config_in_directory(paths.data);
|
|
|
|
source_config_in_directory(paths.sysconf);
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
// We need to get the configuration directory before we can source the user configuration file.
|
|
|
|
// If path_get_config returns false then we have no configuration directory and no custom config
|
|
|
|
// to load.
|
2012-11-19 08:30:30 +08:00
|
|
|
wcstring config_dir;
|
2016-05-01 09:37:19 +08:00
|
|
|
if (path_get_config(config_dir)) {
|
2013-09-30 04:39:41 +08:00
|
|
|
source_config_in_directory(config_dir);
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
Add '--init-command', '-C' to the command line switches.
In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.
The documentation has been updated correspondingly.
The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.
I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.
A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
2017-06-29 22:21:00 +08:00
|
|
|
int run_command_list(std::vector<std::string> *cmds, const io_chain_t &io) {
|
|
|
|
int res = 1;
|
|
|
|
parser_t &parser = parser_t::principal_parser();
|
|
|
|
|
2019-11-20 05:46:47 +08:00
|
|
|
for (const auto &cmd : *cmds) {
|
|
|
|
const wcstring cmd_wcs = str2wcstring(cmd);
|
2019-12-23 08:27:03 +08:00
|
|
|
eval_result_t eval_res = parser.eval(cmd_wcs, io);
|
2019-12-18 07:39:05 +08:00
|
|
|
res = (eval_res == eval_result_t::ok ? 0 : 1);
|
Add '--init-command', '-C' to the command line switches.
In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.
The documentation has been updated correspondingly.
The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.
I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.
A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
2017-06-29 22:21:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-05-16 10:45:02 +08:00
|
|
|
/// Parse the argument list, return the index of the first non-flag arguments.
|
2018-11-28 22:07:58 +08:00
|
|
|
static int fish_parse_opt(int argc, char **argv, fish_cmd_opts_t *opts) {
|
2019-05-05 18:09:25 +08:00
|
|
|
static const char *const short_opts = "+hPilnvc:C:p:d:f:D:";
|
2019-11-19 10:34:50 +08:00
|
|
|
static const struct option long_opts[] = {
|
|
|
|
{"command", required_argument, nullptr, 'c'},
|
|
|
|
{"init-command", required_argument, nullptr, 'C'},
|
|
|
|
{"features", required_argument, nullptr, 'f'},
|
|
|
|
{"debug", required_argument, nullptr, 'd'},
|
|
|
|
{"debug-output", required_argument, nullptr, 'o'},
|
|
|
|
{"debug-stack-frames", required_argument, nullptr, 'D'},
|
|
|
|
{"interactive", no_argument, nullptr, 'i'},
|
|
|
|
{"login", no_argument, nullptr, 'l'},
|
|
|
|
{"no-execute", no_argument, nullptr, 'n'},
|
|
|
|
{"print-rusage-self", no_argument, nullptr, 1},
|
|
|
|
{"print-debug-categories", no_argument, nullptr, 2},
|
|
|
|
{"profile", required_argument, nullptr, 'p'},
|
|
|
|
{"private", no_argument, nullptr, 'P'},
|
|
|
|
{"help", no_argument, nullptr, 'h'},
|
|
|
|
{"version", no_argument, nullptr, 'v'},
|
|
|
|
{nullptr, 0, nullptr, 0}};
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2016-03-04 07:36:17 +08:00
|
|
|
int opt;
|
2019-11-19 10:34:50 +08:00
|
|
|
while ((opt = getopt_long(argc, argv, short_opts, long_opts, nullptr)) != -1) {
|
2016-05-01 09:37:19 +08:00
|
|
|
switch (opt) {
|
|
|
|
case 'c': {
|
Add '--init-command', '-C' to the command line switches.
In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.
The documentation has been updated correspondingly.
The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.
I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.
A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
2017-06-29 22:21:00 +08:00
|
|
|
opts->batch_cmds.push_back(optarg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'C': {
|
|
|
|
opts->postconfig_cmds.push_back(optarg);
|
2012-11-19 16:31:03 +08:00
|
|
|
break;
|
|
|
|
}
|
2016-05-01 09:37:19 +08:00
|
|
|
case 'd': {
|
2012-11-19 16:31:03 +08:00
|
|
|
char *end;
|
|
|
|
long tmp;
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2012-11-19 16:31:03 +08:00
|
|
|
errno = 0;
|
|
|
|
tmp = strtol(optarg, &end, 10);
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
if (tmp >= 0 && tmp <= 10 && !*end && !errno) {
|
2019-11-19 09:08:16 +08:00
|
|
|
debug_level = static_cast<int>(tmp);
|
2016-05-01 09:37:19 +08:00
|
|
|
} else {
|
2019-04-20 15:15:51 +08:00
|
|
|
activate_flog_categories_by_pattern(str2wcstring(optarg));
|
2012-11-19 16:31:03 +08:00
|
|
|
}
|
|
|
|
break;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2019-04-20 15:15:51 +08:00
|
|
|
case 'o': {
|
|
|
|
opts->debug_output = optarg;
|
|
|
|
break;
|
|
|
|
}
|
2018-04-25 05:32:06 +08:00
|
|
|
case 'f': {
|
|
|
|
opts->features = str2wcstring(optarg);
|
|
|
|
break;
|
|
|
|
}
|
2016-05-01 09:37:19 +08:00
|
|
|
case 'h': {
|
Add '--init-command', '-C' to the command line switches.
In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.
The documentation has been updated correspondingly.
The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.
I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.
A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
2017-06-29 22:21:00 +08:00
|
|
|
opts->batch_cmds.push_back("__fish_print_help fish");
|
2012-11-19 16:31:03 +08:00
|
|
|
break;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2016-05-01 09:37:19 +08:00
|
|
|
case 'i': {
|
2019-05-13 06:48:00 +08:00
|
|
|
opts->is_interactive_session = true;
|
2012-11-19 16:31:03 +08:00
|
|
|
break;
|
|
|
|
}
|
2016-05-01 09:37:19 +08:00
|
|
|
case 'l': {
|
2019-05-13 06:48:00 +08:00
|
|
|
opts->is_login = true;
|
2012-11-19 16:31:03 +08:00
|
|
|
break;
|
|
|
|
}
|
2016-05-01 09:37:19 +08:00
|
|
|
case 'n': {
|
2019-05-13 06:48:00 +08:00
|
|
|
opts->no_exec = true;
|
2012-11-19 16:31:03 +08:00
|
|
|
break;
|
|
|
|
}
|
2019-04-11 04:58:29 +08:00
|
|
|
case 1: {
|
|
|
|
opts->print_rusage_self = true;
|
|
|
|
break;
|
|
|
|
}
|
2019-04-20 15:15:51 +08:00
|
|
|
case 2: {
|
|
|
|
auto cats = get_flog_categories();
|
|
|
|
// Compute width of longest name.
|
|
|
|
int name_width = 0;
|
|
|
|
for (const auto *cat : cats) {
|
2019-11-19 09:08:16 +08:00
|
|
|
name_width = std::max(name_width, static_cast<int>(wcslen(cat->name)));
|
2019-04-20 15:15:51 +08:00
|
|
|
}
|
|
|
|
// A little extra space.
|
|
|
|
name_width += 2;
|
|
|
|
for (const auto *cat : cats) {
|
|
|
|
// Negating the name width left-justifies.
|
|
|
|
printf("%*ls %ls\n", -name_width, cat->name, _(cat->description));
|
|
|
|
}
|
|
|
|
exit(0);
|
|
|
|
break;
|
|
|
|
}
|
2016-05-01 09:37:19 +08:00
|
|
|
case 'p': {
|
2014-02-10 06:04:43 +08:00
|
|
|
s_profiling_output_filename = optarg;
|
|
|
|
g_profiling_active = true;
|
2012-11-19 16:31:03 +08:00
|
|
|
break;
|
|
|
|
}
|
2018-09-20 07:56:08 +08:00
|
|
|
case 'P': {
|
|
|
|
start_private_mode();
|
|
|
|
break;
|
|
|
|
}
|
2016-05-01 09:37:19 +08:00
|
|
|
case 'v': {
|
2019-03-13 05:06:01 +08:00
|
|
|
std::fwprintf(stdout, _(L"%s, version %s\n"), PACKAGE_NAME, get_fish_version());
|
2016-03-24 02:42:17 +08:00
|
|
|
exit(0);
|
2016-11-02 13:36:30 +08:00
|
|
|
break;
|
2012-11-19 16:31:03 +08:00
|
|
|
}
|
2016-05-16 10:45:02 +08:00
|
|
|
case 'D': {
|
|
|
|
char *end;
|
|
|
|
long tmp;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
tmp = strtol(optarg, &end, 10);
|
|
|
|
|
|
|
|
if (tmp > 0 && tmp <= 128 && !*end && !errno) {
|
2019-11-19 09:08:16 +08:00
|
|
|
set_debug_stack_frames(static_cast<int>(tmp));
|
2016-05-16 10:45:02 +08:00
|
|
|
} else {
|
2019-05-05 18:09:25 +08:00
|
|
|
std::fwprintf(stderr, _(L"Invalid value '%s' for debug-stack-frames flag"),
|
|
|
|
optarg);
|
2016-05-16 10:45:02 +08:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2016-05-01 09:37:19 +08:00
|
|
|
default: {
|
2016-03-04 07:36:17 +08:00
|
|
|
// We assume getopt_long() has already emitted a diagnostic msg.
|
2016-03-24 02:42:17 +08:00
|
|
|
exit(1);
|
2016-11-02 13:36:30 +08:00
|
|
|
break;
|
2012-11-19 16:31:03 +08:00
|
|
|
}
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-19 12:05:00 +08:00
|
|
|
// If our command name begins with a dash that implies we're a login shell.
|
2019-05-13 06:48:00 +08:00
|
|
|
opts->is_login |= argv[0][0] == '-';
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2015-12-19 12:05:00 +08:00
|
|
|
// We are an interactive session if we have not been given an explicit
|
|
|
|
// command or file to execute and stdin is a tty. Note that the -i or
|
|
|
|
// --interactive options also force interactive mode.
|
2019-11-19 08:56:46 +08:00
|
|
|
if (opts->batch_cmds.empty() && optind == argc && isatty(STDIN_FILENO)) {
|
2019-05-13 06:48:00 +08:00
|
|
|
set_interactive_session(true);
|
2013-10-27 06:24:49 +08:00
|
|
|
}
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2015-12-19 12:05:00 +08:00
|
|
|
return optind;
|
2006-10-26 04:54:43 +08:00
|
|
|
}
|
|
|
|
|
2018-11-28 22:07:58 +08:00
|
|
|
int main(int argc, char **argv) {
|
2016-05-01 09:37:19 +08:00
|
|
|
int res = 1;
|
|
|
|
int my_optind = 0;
|
2006-10-26 04:54:43 +08:00
|
|
|
|
2018-11-28 22:08:24 +08:00
|
|
|
program_name = L"fish";
|
2012-11-19 08:30:30 +08:00
|
|
|
set_main_thread();
|
2012-02-28 10:43:24 +08:00
|
|
|
setup_fork_guards();
|
2017-05-03 11:55:41 +08:00
|
|
|
signal_unblock_all();
|
2016-06-04 10:05:13 +08:00
|
|
|
setlocale(LC_ALL, "");
|
2006-10-26 04:54:43 +08:00
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
// struct stat tmp;
|
|
|
|
// stat("----------FISH_HIT_MAIN----------", &tmp);
|
2006-11-11 18:48:40 +08:00
|
|
|
|
2019-11-19 10:34:50 +08:00
|
|
|
const char *dummy_argv[2] = {"fish", nullptr};
|
2018-11-28 22:08:24 +08:00
|
|
|
if (!argv[0]) {
|
|
|
|
argv = (char **)dummy_argv; //!OCLINT(parameter reassignment)
|
|
|
|
argc = 1; //!OCLINT(parameter reassignment)
|
|
|
|
}
|
2019-05-13 06:48:00 +08:00
|
|
|
fish_cmd_opts_t opts{};
|
Add '--init-command', '-C' to the command line switches.
In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.
The documentation has been updated correspondingly.
The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.
I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.
A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
2017-06-29 22:21:00 +08:00
|
|
|
my_optind = fish_parse_opt(argc, argv, &opts);
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2019-04-20 15:15:51 +08:00
|
|
|
// Direct any debug output right away.
|
|
|
|
FILE *debug_output = nullptr;
|
|
|
|
if (!opts.debug_output.empty()) {
|
|
|
|
debug_output = fopen(opts.debug_output.c_str(), "w");
|
|
|
|
if (!debug_output) {
|
|
|
|
fprintf(stderr, "Could not open file %s\n", opts.debug_output.c_str());
|
|
|
|
perror("fopen");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
set_cloexec(fileno(debug_output));
|
|
|
|
setlinebuf(debug_output);
|
|
|
|
set_flog_output_file(debug_output);
|
|
|
|
}
|
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
// No-exec is prohibited when in interactive mode.
|
2019-05-13 06:48:00 +08:00
|
|
|
if (opts.is_interactive_session && opts.no_exec) {
|
2012-11-19 08:30:30 +08:00
|
|
|
debug(1, _(L"Can not use the no-execute mode when running an interactive session"));
|
2019-05-13 06:48:00 +08:00
|
|
|
opts.no_exec = false;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2013-10-27 06:27:39 +08:00
|
|
|
|
2019-05-13 06:48:00 +08:00
|
|
|
// Apply our options.
|
|
|
|
if (opts.is_login) mark_login();
|
|
|
|
if (opts.no_exec) mark_no_exec();
|
|
|
|
if (opts.is_interactive_session) set_interactive_session(true);
|
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
// Only save (and therefore restore) the fg process group if we are interactive. See issues
|
|
|
|
// #197 and #1002.
|
2019-05-13 06:48:00 +08:00
|
|
|
if (is_interactive_session()) {
|
2013-10-27 06:22:20 +08:00
|
|
|
save_term_foreground_process_group();
|
|
|
|
}
|
|
|
|
|
2018-11-28 22:07:58 +08:00
|
|
|
const struct config_paths_t paths = determine_config_directory_paths(argv[0]);
|
2017-02-16 12:09:26 +08:00
|
|
|
env_init(&paths);
|
2018-04-25 05:32:06 +08:00
|
|
|
// Set features early in case other initialization depends on them.
|
2018-04-25 07:48:13 +08:00
|
|
|
// Start with the ones set in the environment, then those set on the command line (so the
|
|
|
|
// command line takes precedence).
|
2019-05-10 09:04:30 +08:00
|
|
|
if (auto features_var = env_stack_t::globals().get(L"fish_features")) {
|
2018-04-25 07:48:13 +08:00
|
|
|
for (const wcstring &s : features_var->as_list()) {
|
|
|
|
mutable_fish_features().set_from_string(s);
|
|
|
|
}
|
|
|
|
}
|
2018-04-25 05:32:06 +08:00
|
|
|
mutable_fish_features().set_from_string(opts.features);
|
2012-11-19 08:30:30 +08:00
|
|
|
proc_init();
|
|
|
|
builtin_init();
|
2017-02-16 12:09:26 +08:00
|
|
|
misc_init();
|
2012-11-19 08:30:30 +08:00
|
|
|
reader_init();
|
2006-03-10 21:38:09 +08:00
|
|
|
|
2012-01-23 13:40:08 +08:00
|
|
|
parser_t &parser = parser_t::principal_parser();
|
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
if (read_init(paths)) {
|
|
|
|
// Stomp the exit status of any initialization commands (issue #635).
|
2019-05-13 05:00:44 +08:00
|
|
|
parser.set_last_statuses(statuses_t::just(STATUS_CMD_OK));
|
2013-05-05 17:33:17 +08:00
|
|
|
|
Add '--init-command', '-C' to the command line switches.
In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.
The documentation has been updated correspondingly.
The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.
I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.
A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
2017-06-29 22:21:00 +08:00
|
|
|
// Run post-config commands specified as arguments, if any.
|
|
|
|
if (!opts.postconfig_cmds.empty()) {
|
2019-02-03 09:45:21 +08:00
|
|
|
res = run_command_list(&opts.postconfig_cmds, {});
|
Add '--init-command', '-C' to the command line switches.
In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.
The documentation has been updated correspondingly.
The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.
I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.
A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
2017-06-29 22:21:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!opts.batch_cmds.empty()) {
|
|
|
|
// Run the commands specified as arguments, if any.
|
2019-05-13 06:48:00 +08:00
|
|
|
if (get_login()) {
|
Add '--init-command', '-C' to the command line switches.
In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.
The documentation has been updated correspondingly.
The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.
I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.
A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
2017-06-29 22:21:00 +08:00
|
|
|
// Do something nasty to support OpenSUSE assuming we're bash. This may modify cmds.
|
|
|
|
fish_xdm_login_hack_hack_hack_hack(&opts.batch_cmds, argc - my_optind,
|
|
|
|
argv + my_optind);
|
2013-01-05 05:09:01 +08:00
|
|
|
}
|
2019-02-03 09:45:21 +08:00
|
|
|
res = run_command_list(&opts.batch_cmds, {});
|
2019-02-25 05:24:03 +08:00
|
|
|
reader_set_end_loop(false);
|
2016-05-01 09:37:19 +08:00
|
|
|
} else if (my_optind == argc) {
|
Add '--init-command', '-C' to the command line switches.
In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.
The documentation has been updated correspondingly.
The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.
I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.
A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
2017-06-29 22:21:00 +08:00
|
|
|
// Implicitly interactive mode.
|
2019-05-27 09:51:26 +08:00
|
|
|
res = reader_read(parser, STDIN_FILENO, {});
|
2016-05-01 09:37:19 +08:00
|
|
|
} else {
|
|
|
|
char *file = *(argv + (my_optind++));
|
2016-03-16 05:03:27 +08:00
|
|
|
int fd = open(file, O_RDONLY);
|
2016-05-01 09:37:19 +08:00
|
|
|
if (fd == -1) {
|
2016-03-16 05:03:27 +08:00
|
|
|
perror(file);
|
2016-05-01 09:37:19 +08:00
|
|
|
} else {
|
2019-04-14 03:22:46 +08:00
|
|
|
// OK to not do this atomically since we cannot have gone multithreaded yet.
|
|
|
|
set_cloexec(fd);
|
|
|
|
|
2017-07-09 04:14:30 +08:00
|
|
|
wcstring_list_t list;
|
2018-11-28 22:07:58 +08:00
|
|
|
for (char **ptr = argv + my_optind; *ptr; ptr++) {
|
2017-07-09 04:14:30 +08:00
|
|
|
list.push_back(str2wcstring(*ptr));
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2018-09-14 15:36:26 +08:00
|
|
|
parser.vars().set(L"argv", ENV_DEFAULT, list);
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2019-05-23 04:34:03 +08:00
|
|
|
auto &ld = parser.libdata();
|
|
|
|
wcstring rel_filename = str2wcstring(file);
|
|
|
|
scoped_push<const wchar_t *> filename_push{&ld.current_filename,
|
|
|
|
intern(rel_filename.c_str())};
|
2019-05-27 09:51:26 +08:00
|
|
|
res = reader_read(parser, fd, {});
|
2016-05-01 09:37:19 +08:00
|
|
|
if (res) {
|
2017-02-14 12:37:27 +08:00
|
|
|
debug(1, _(L"Error while reading file %ls\n"),
|
2019-05-23 04:34:03 +08:00
|
|
|
ld.current_filename ? ld.current_filename : _(L"Standard input"));
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-13 05:00:44 +08:00
|
|
|
int exit_status = res ? STATUS_CMD_UNKNOWN : parser.get_last_status();
|
2014-10-28 08:23:08 +08:00
|
|
|
|
2019-06-03 17:31:13 +08:00
|
|
|
event_fire(parser,
|
|
|
|
proc_create_event(L"PROCESS_EXIT", event_type_t::exit, getpid(), exit_status));
|
2019-01-04 12:20:43 +08:00
|
|
|
|
|
|
|
// Trigger any exit handlers.
|
2019-02-04 08:06:10 +08:00
|
|
|
wcstring_list_t event_args = {to_string(exit_status)};
|
2019-06-03 17:31:13 +08:00
|
|
|
event_fire_generic(parser, L"fish_exit", &event_args);
|
2013-10-27 06:27:39 +08:00
|
|
|
|
2013-10-27 06:22:20 +08:00
|
|
|
restore_term_mode();
|
2012-11-18 18:16:14 +08:00
|
|
|
restore_term_foreground_process_group();
|
2014-04-01 01:01:39 +08:00
|
|
|
|
2016-05-01 09:37:19 +08:00
|
|
|
if (g_profiling_active) {
|
2014-02-10 06:04:43 +08:00
|
|
|
parser.emit_profiling(s_profiling_output_filename);
|
|
|
|
}
|
2014-04-01 01:01:39 +08:00
|
|
|
|
2018-09-29 08:20:54 +08:00
|
|
|
history_save_all();
|
2019-04-11 04:58:29 +08:00
|
|
|
if (opts.print_rusage_self) {
|
|
|
|
print_rusage_self(stderr);
|
|
|
|
}
|
2019-04-20 15:15:51 +08:00
|
|
|
if (debug_output) {
|
|
|
|
fclose(debug_output);
|
|
|
|
}
|
2014-10-28 08:23:08 +08:00
|
|
|
exit_without_destructors(exit_status);
|
2016-05-01 09:37:19 +08:00
|
|
|
return EXIT_FAILURE; // above line should always exit
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|