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 14:21:00 +00:00
|
|
|
//
|
2016-05-01 01:37:19 +00:00
|
|
|
// The main loop of the fish program.
|
2005-09-20 13:26:39 +00:00
|
|
|
/*
|
2008-01-12 19:18:48 +00:00
|
|
|
Copyright (C) 2005-2008 Axel Liljencrantz
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2006-11-01 14:47:47 +00: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 13:26:39 +00: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-13 20:51:52 +00:00
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
2005-09-20 13:26:39 +00:00
|
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
|
2016-05-01 01:37:19 +00:00
|
|
|
#include <fcntl.h>
|
2016-03-03 23:36:17 +00:00
|
|
|
#include <getopt.h>
|
2015-07-25 15:14:25 +00:00
|
|
|
#include <limits.h>
|
2016-05-01 01:37:19 +00:00
|
|
|
#include <locale.h>
|
2005-09-20 13:26:39 +00:00
|
|
|
#include <stdio.h>
|
2016-05-01 01:37:19 +00:00
|
|
|
#include <stdlib.h>
|
2022-08-21 06:14:48 +00:00
|
|
|
#include <strings.h>
|
2019-04-10 20:58:29 +00:00
|
|
|
#include <sys/resource.h>
|
2016-05-01 01:37:19 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
2019-10-13 22:50:48 +00:00
|
|
|
|
2022-08-21 06:14:48 +00:00
|
|
|
#include <algorithm>
|
2019-05-05 10:09:25 +00:00
|
|
|
#include <cstring>
|
2019-03-12 21:06:01 +00:00
|
|
|
#include <cwchar>
|
2016-04-21 06:00:54 +00:00
|
|
|
#include <memory>
|
2016-05-01 01:37:19 +00:00
|
|
|
#include <string>
|
2022-08-21 06:14:48 +00:00
|
|
|
#include <utility>
|
2016-05-01 01:37:19 +00:00
|
|
|
#include <vector>
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2022-08-21 06:14:48 +00:00
|
|
|
#include "ast.h"
|
2016-05-01 01:37:19 +00:00
|
|
|
#include "common.h"
|
2023-01-14 22:56:24 +00:00
|
|
|
#include "cxxgen.h"
|
2005-09-20 13:26:39 +00:00
|
|
|
#include "env.h"
|
2005-10-05 22:37:08 +00:00
|
|
|
#include "event.h"
|
2016-05-01 01:37:19 +00:00
|
|
|
#include "expand.h"
|
|
|
|
#include "fallback.h" // IWYU pragma: keep
|
2022-08-21 06:14:48 +00:00
|
|
|
#include "fds.h"
|
Port AST to Rust
The translation is fairly direct though it adds some duplication, for example
there are multiple "match" statements that mimic function overloading.
Rust has no overloading, and we cannot have generic methods in the Node trait
(due to a Rust limitation, the error is like "cannot be made into an object")
so we include the type name in method names.
Give clients like "indent_visitor_t" a Rust companion ("IndentVisitor")
that takes care of the AST traversal while the AST consumption remains
in C++ for now. In future, "IndentVisitor" should absorb the entirety of
"indent_visitor_t". This pattern requires that "fish_indent" be exposed
includable header to the CXX bridge.
Alternatively, we could define FFI wrappers for recursive AST traversal.
Rust requires we separate the AST visitors for "mut" and "const"
scenarios. Take this opportunity to concretize both visitors:
The only client that requires mutable access is the populator. To match the
structure of the C++ populator which makes heavy use of function overloading,
we need to add a bunch of functions to the trait. Since there is no other
mutable visit, this seems acceptable.
The "const" visitors never use "will_visit_fields_of()" or
"did_visit_fields_of()", so remove them (though this is debatable).
Like in the C++ implementation, the AST nodes themselves are largely defined
via macros. Union fields like "Statement" and "ArgumentOrRedirection"
do currently not use macros but may in future.
This commit also introduces a precedent for a type that is defined in one
CXX bridge and used in another one - "ParseErrorList". To make this work
we need to manually define "ExternType".
There is one annoyance with CXX: functions that take explicit lifetime
parameters require to be marked as unsafe. This makes little sense
because functions that return `&Foo` with implicit lifetime can be
misused the same way on the C++ side.
One notable change is that we cannot directly port "find_block_open_keyword()"
(which is used to compute an error) because it relies on the stack of visited
nodes. We cannot modify a stack of node references while we do the "mut"
walk. Happily, an idiomatic solution is easy: we can tell the AST visitor
to backtrack to the parent node and create the error there.
Since "node_t::accept_base" is no longer a template we don't need the
"node_visitation_t" trampoline anymore.
The added copying at the FFI boundary makes things slower (memcpy dominates
the profile) but it's not unusable, which is good news:
$ hyperfine ./fish.{old,new}" -c 'source ../share/completions/git.fish'"
Benchmark 1: ./fish.old -c 'source ../share/completions/git.fish'
Time (mean ± σ): 195.5 ms ± 2.9 ms [User: 190.1 ms, System: 4.4 ms]
Range (min … max): 193.2 ms … 205.1 ms 15 runs
Benchmark 2: ./fish.new -c 'source ../share/completions/git.fish'
Time (mean ± σ): 677.5 ms ± 62.0 ms [User: 665.4 ms, System: 10.0 ms]
Range (min … max): 611.7 ms … 805.5 ms 10 runs
Summary
'./fish.old -c 'source ../share/completions/git.fish'' ran
3.47 ± 0.32 times faster than './fish.new -c 'source ../share/completions/git.fish''
Leftovers:
- Enum variants are still snakecase; I didn't get around to changing this yet.
- "ast_type_to_string()" still returns a snakecase name. This could be
changed since it's not user visible.
2023-04-02 14:42:59 +00:00
|
|
|
#include "ffi_baggage.h"
|
2023-01-14 22:56:24 +00:00
|
|
|
#include "ffi_init.rs.h"
|
2016-05-01 01:37:19 +00:00
|
|
|
#include "fish_version.h"
|
2019-04-20 07:15:51 +00:00
|
|
|
#include "flog.h"
|
2016-05-01 01:37:19 +00:00
|
|
|
#include "function.h"
|
2023-02-07 22:18:51 +00:00
|
|
|
#include "future_feature_flags.h"
|
2022-08-21 06:14:48 +00:00
|
|
|
#include "global_safety.h"
|
2006-04-19 23:42:11 +00:00
|
|
|
#include "history.h"
|
2016-05-01 01:37:19 +00:00
|
|
|
#include "io.h"
|
2022-08-21 06:14:48 +00:00
|
|
|
#include "maybe.h"
|
|
|
|
#include "parse_constants.h"
|
|
|
|
#include "parse_tree.h"
|
2021-07-27 15:44:02 +00:00
|
|
|
#include "parse_util.h"
|
2021-11-08 19:36:01 +00:00
|
|
|
#include "parser.h"
|
2016-05-01 01:37:19 +00:00
|
|
|
#include "path.h"
|
|
|
|
#include "proc.h"
|
|
|
|
#include "reader.h"
|
2023-01-14 22:56:24 +00:00
|
|
|
#include "signals.h"
|
2023-04-29 17:07:59 +00:00
|
|
|
#include "threads.rs.h"
|
2020-01-15 21:16:43 +00:00
|
|
|
#include "wcstringutil.h"
|
2016-05-01 01:37:19 +00:00
|
|
|
#include "wutil.h" // IWYU pragma: keep
|
2005-09-20 13:26:39 +00: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 14:21:00 +00:00
|
|
|
// container to hold the options specified within the command line
|
|
|
|
class fish_cmd_opts_t {
|
2017-07-19 05:40:25 +00:00
|
|
|
public:
|
2018-04-24 21:32:06 +00:00
|
|
|
// Future feature flags values string
|
|
|
|
wcstring features;
|
2019-04-20 07:15:51 +00:00
|
|
|
// File path for debug output.
|
|
|
|
std::string debug_output;
|
2020-07-22 22:35:14 +00:00
|
|
|
// File path for profiling output, or empty for none.
|
|
|
|
std::string profile_output;
|
2021-01-08 16:25:00 +00:00
|
|
|
std::string profile_startup_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 14:21:00 +00: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-10 20:58:29 +00:00
|
|
|
/// Whether to print rusage-self stats after execution.
|
|
|
|
bool print_rusage_self{false};
|
2021-02-15 19:57:11 +00:00
|
|
|
/// Whether no-config is set.
|
|
|
|
bool no_config{false};
|
2019-05-12 22:48:00 +00: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};
|
2020-02-08 20:23:39 +00:00
|
|
|
/// Whether to enable private mode.
|
|
|
|
bool enable_private_mode{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 14:21:00 +00:00
|
|
|
};
|
|
|
|
|
2019-04-10 20:58:29 +00:00
|
|
|
/// \return a timeval converted to milliseconds.
|
2020-09-08 20:04:44 +00:00
|
|
|
static long long tv_to_msec(const struct timeval &tv) {
|
2019-11-19 01:08:16 +00: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-10 20:58:29 +00: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");
|
2020-03-13 21:02:37 +00:00
|
|
|
fprintf(fp, " user time: %lld ms\n", tv_to_msec(rs.ru_utime));
|
|
|
|
fprintf(fp, " sys time: %lld ms\n", tv_to_msec(rs.ru_stime));
|
|
|
|
fprintf(fp, " total time: %lld ms\n", tv_to_msec(rs.ru_utime) + tv_to_msec(rs.ru_stime));
|
2019-04-10 20:58:29 +00:00
|
|
|
fprintf(fp, " max rss: %ld kb\n", rss_kb);
|
|
|
|
fprintf(fp, " signals: %ld\n", rs.ru_nsignals);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-05-01 01:37:19 +00:00
|
|
|
static bool has_suffix(const std::string &path, const char *suffix, bool ignore_case) {
|
2019-03-12 22:07:07 +00:00
|
|
|
size_t pathlen = path.size(), suffixlen = std::strlen(suffix);
|
2016-05-01 01:37:19 +00:00
|
|
|
return pathlen >= suffixlen &&
|
2019-03-12 22:07:07 +00:00
|
|
|
!(ignore_case ? strcasecmp : std::strcmp)(path.c_str() + pathlen - suffixlen, suffix);
|
2012-07-08 22:20:39 +00:00
|
|
|
}
|
|
|
|
|
2016-05-01 01:37:19 +00:00
|
|
|
/// Modifies the given path by calling realpath. Returns true if realpath succeeded, false
|
|
|
|
/// otherwise.
|
|
|
|
static bool get_realpath(std::string &path) {
|
2012-07-08 22:20:39 +00:00
|
|
|
char buff[PATH_MAX], *ptr;
|
2016-05-01 01:37:19 +00:00
|
|
|
if ((ptr = realpath(path.c_str(), buff))) {
|
2012-07-08 22:20:39 +00:00
|
|
|
path = ptr;
|
|
|
|
}
|
2019-11-19 02:34:50 +00:00
|
|
|
return ptr != nullptr;
|
2012-07-08 22:20:39 +00:00
|
|
|
}
|
|
|
|
|
2016-05-01 01:37:19 +00:00
|
|
|
static struct config_paths_t determine_config_directory_paths(const char *argv0) {
|
2012-07-08 22:20:39 +00:00
|
|
|
struct config_paths_t paths;
|
|
|
|
bool done = false;
|
|
|
|
std::string exec_path = get_executable_path(argv0);
|
2016-05-01 01:37:19 +00:00
|
|
|
if (get_realpath(exec_path)) {
|
2020-01-19 12:53:23 +00:00
|
|
|
FLOGF(config, L"exec_path: '%s', argv[0]: '%s'", exec_path.c_str(), argv0);
|
2018-11-25 22:06:32 +00:00
|
|
|
// TODO: we should determine program_name from argv0 somewhere in this file
|
2018-10-14 02:36:59 +00:00
|
|
|
|
|
|
|
#ifdef CMAKE_BINARY_DIR
|
|
|
|
// Detect if we're running right out of the CMAKE build directory
|
2018-11-25 22:06:32 +00:00
|
|
|
if (string_prefixes_string(CMAKE_BINARY_DIR, exec_path.c_str())) {
|
2020-01-19 12:53:23 +00:00
|
|
|
FLOGF(config,
|
2019-05-05 10:09:25 +00:00
|
|
|
"Running out of build directory, using paths relative to CMAKE_SOURCE_DIR:\n %s",
|
|
|
|
CMAKE_SOURCE_DIR);
|
2018-10-14 02:36:59 +00:00
|
|
|
|
|
|
|
done = true;
|
|
|
|
paths.data = wcstring{L"" CMAKE_SOURCE_DIR} + L"/share";
|
2018-11-25 22:06:32 +00: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 02:36:59 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-05-01 01:37:19 +00:00
|
|
|
if (!done) {
|
2016-09-23 18:52:51 +00: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 02:34:50 +00:00
|
|
|
const char *suffix = nullptr;
|
2016-09-23 18:52:51 +00:00
|
|
|
|
|
|
|
if (has_suffix(exec_path, installed_suffix, false)) {
|
|
|
|
suffix = installed_suffix;
|
|
|
|
} else if (has_suffix(exec_path, just_a_fish, false)) {
|
2020-01-19 12:53:23 +00:00
|
|
|
FLOGF(config, L"'fish' not in a 'bin/', trying paths relative to source tree");
|
2016-09-23 18:52:51 +00:00
|
|
|
suffix = just_a_fish;
|
2012-07-08 22:20:39 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-09-23 18:52:51 +00:00
|
|
|
if (suffix) {
|
|
|
|
bool seems_installed = (suffix == installed_suffix);
|
|
|
|
|
2012-07-08 22:20:39 +00:00
|
|
|
wcstring base_path = str2wcstring(exec_path);
|
2019-03-12 22:07:07 +00:00
|
|
|
base_path.resize(base_path.size() - std::strlen(suffix));
|
2014-03-31 17:01:39 +00:00
|
|
|
|
2016-09-23 18:52:51 +00: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-03-31 17:01:39 +00:00
|
|
|
|
2018-07-22 03:57:21 +00: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 01:37:19 +00:00
|
|
|
// The docs dir may not exist; in that case fall back to the compiled in path.
|
2018-07-22 03:57:21 +00:00
|
|
|
if (0 != wstat(paths.doc, &buf)) {
|
2014-01-17 22:18:45 +00:00
|
|
|
paths.doc = L"" DOCDIR;
|
|
|
|
}
|
2012-07-08 22:20:39 +00:00
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-05-01 01:37:19 +00:00
|
|
|
if (!done) {
|
|
|
|
// Fall back to what got compiled in.
|
2020-01-19 12:53:23 +00:00
|
|
|
FLOGF(config, L"Using compiled in paths:");
|
2012-07-08 22:20:39 +00:00
|
|
|
paths.data = L"" DATADIR "/fish";
|
|
|
|
paths.sysconf = L"" SYSCONFDIR "/fish";
|
2013-10-28 16:01:21 +00:00
|
|
|
paths.doc = L"" DOCDIR;
|
2013-04-16 05:22:19 +00:00
|
|
|
paths.bin = L"" BINDIR;
|
2012-07-08 22:20:39 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2020-01-19 12:53:23 +00:00
|
|
|
FLOGF(config,
|
2016-10-05 03:06:14 +00:00
|
|
|
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-08 22:20:39 +00:00
|
|
|
return paths;
|
|
|
|
}
|
|
|
|
|
2016-02-04 20:51:55 +00:00
|
|
|
// Source the file config.fish in the given directory.
|
2020-02-08 20:23:39 +00:00
|
|
|
static void source_config_in_directory(parser_t &parser, const wcstring &dir) {
|
2016-05-01 01:37:19 +00:00
|
|
|
// 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-04 20:51:55 +00:00
|
|
|
//
|
2016-05-01 01:37:19 +00: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-04 20:51:55 +00:00
|
|
|
const wcstring config_pathname = dir + L"/config.fish";
|
2022-07-25 14:25:04 +00:00
|
|
|
const wcstring escaped_pathname = escape_string(dir) + L"/config.fish";
|
2016-02-04 20:51:55 +00:00
|
|
|
if (waccess(config_pathname, R_OK) != 0) {
|
2020-01-30 01:06:11 +00:00
|
|
|
FLOGF(config, L"not sourcing %ls (not readable or does not exist)",
|
|
|
|
escaped_pathname.c_str());
|
2016-02-04 20:51:55 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-01-19 12:53:23 +00:00
|
|
|
FLOGF(config, L"sourcing %ls", escaped_pathname.c_str());
|
2016-02-04 20:51:55 +00:00
|
|
|
|
|
|
|
const wcstring cmd = L"builtin source " + escaped_pathname;
|
2022-03-25 04:43:58 +00:00
|
|
|
|
|
|
|
parser.libdata().within_fish_init = true;
|
2019-12-23 00:27:03 +00:00
|
|
|
parser.eval(cmd, io_chain_t());
|
2022-03-25 04:43:58 +00:00
|
|
|
parser.libdata().within_fish_init = false;
|
2013-09-29 20:39:41 +00:00
|
|
|
}
|
|
|
|
|
2016-05-01 01:37:19 +00:00
|
|
|
/// Parse init files. exec_path is the path of fish executable as determined by argv[0].
|
2020-04-03 19:24:31 +00:00
|
|
|
static void read_init(parser_t &parser, const struct config_paths_t &paths) {
|
2020-02-08 20:23:39 +00:00
|
|
|
source_config_in_directory(parser, paths.data);
|
|
|
|
source_config_in_directory(parser, paths.sysconf);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-05-01 01:37:19 +00: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 00:30:30 +00:00
|
|
|
wcstring config_dir;
|
2016-05-01 01:37:19 +00:00
|
|
|
if (path_get_config(config_dir)) {
|
2020-02-08 20:23:39 +00:00
|
|
|
source_config_in_directory(parser, config_dir);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2022-06-20 19:55:33 +00:00
|
|
|
static int run_command_list(parser_t &parser, const std::vector<std::string> &cmds,
|
2020-09-08 20:04:44 +00:00
|
|
|
const io_chain_t &io) {
|
2022-06-20 19:55:33 +00:00
|
|
|
for (const auto &cmd : cmds) {
|
2020-02-08 20:23:39 +00:00
|
|
|
wcstring cmd_wcs = str2wcstring(cmd);
|
2021-07-27 15:44:02 +00:00
|
|
|
// Parse into an ast and detect errors.
|
2023-02-05 08:35:06 +00:00
|
|
|
auto errors = new_parse_error_list();
|
Port AST to Rust
The translation is fairly direct though it adds some duplication, for example
there are multiple "match" statements that mimic function overloading.
Rust has no overloading, and we cannot have generic methods in the Node trait
(due to a Rust limitation, the error is like "cannot be made into an object")
so we include the type name in method names.
Give clients like "indent_visitor_t" a Rust companion ("IndentVisitor")
that takes care of the AST traversal while the AST consumption remains
in C++ for now. In future, "IndentVisitor" should absorb the entirety of
"indent_visitor_t". This pattern requires that "fish_indent" be exposed
includable header to the CXX bridge.
Alternatively, we could define FFI wrappers for recursive AST traversal.
Rust requires we separate the AST visitors for "mut" and "const"
scenarios. Take this opportunity to concretize both visitors:
The only client that requires mutable access is the populator. To match the
structure of the C++ populator which makes heavy use of function overloading,
we need to add a bunch of functions to the trait. Since there is no other
mutable visit, this seems acceptable.
The "const" visitors never use "will_visit_fields_of()" or
"did_visit_fields_of()", so remove them (though this is debatable).
Like in the C++ implementation, the AST nodes themselves are largely defined
via macros. Union fields like "Statement" and "ArgumentOrRedirection"
do currently not use macros but may in future.
This commit also introduces a precedent for a type that is defined in one
CXX bridge and used in another one - "ParseErrorList". To make this work
we need to manually define "ExternType".
There is one annoyance with CXX: functions that take explicit lifetime
parameters require to be marked as unsafe. This makes little sense
because functions that return `&Foo` with implicit lifetime can be
misused the same way on the C++ side.
One notable change is that we cannot directly port "find_block_open_keyword()"
(which is used to compute an error) because it relies on the stack of visited
nodes. We cannot modify a stack of node references while we do the "mut"
walk. Happily, an idiomatic solution is easy: we can tell the AST visitor
to backtrack to the parent node and create the error there.
Since "node_t::accept_base" is no longer a template we don't need the
"node_visitation_t" trampoline anymore.
The added copying at the FFI boundary makes things slower (memcpy dominates
the profile) but it's not unusable, which is good news:
$ hyperfine ./fish.{old,new}" -c 'source ../share/completions/git.fish'"
Benchmark 1: ./fish.old -c 'source ../share/completions/git.fish'
Time (mean ± σ): 195.5 ms ± 2.9 ms [User: 190.1 ms, System: 4.4 ms]
Range (min … max): 193.2 ms … 205.1 ms 15 runs
Benchmark 2: ./fish.new -c 'source ../share/completions/git.fish'
Time (mean ± σ): 677.5 ms ± 62.0 ms [User: 665.4 ms, System: 10.0 ms]
Range (min … max): 611.7 ms … 805.5 ms 10 runs
Summary
'./fish.old -c 'source ../share/completions/git.fish'' ran
3.47 ± 0.32 times faster than './fish.new -c 'source ../share/completions/git.fish''
Leftovers:
- Enum variants are still snakecase; I didn't get around to changing this yet.
- "ast_type_to_string()" still returns a snakecase name. This could be
changed since it's not user visible.
2023-04-02 14:42:59 +00:00
|
|
|
auto ast = ast_parse(cmd_wcs, parse_flag_none, &*errors);
|
|
|
|
bool errored = ast->errored();
|
2021-07-27 15:44:02 +00:00
|
|
|
if (!errored) {
|
Port AST to Rust
The translation is fairly direct though it adds some duplication, for example
there are multiple "match" statements that mimic function overloading.
Rust has no overloading, and we cannot have generic methods in the Node trait
(due to a Rust limitation, the error is like "cannot be made into an object")
so we include the type name in method names.
Give clients like "indent_visitor_t" a Rust companion ("IndentVisitor")
that takes care of the AST traversal while the AST consumption remains
in C++ for now. In future, "IndentVisitor" should absorb the entirety of
"indent_visitor_t". This pattern requires that "fish_indent" be exposed
includable header to the CXX bridge.
Alternatively, we could define FFI wrappers for recursive AST traversal.
Rust requires we separate the AST visitors for "mut" and "const"
scenarios. Take this opportunity to concretize both visitors:
The only client that requires mutable access is the populator. To match the
structure of the C++ populator which makes heavy use of function overloading,
we need to add a bunch of functions to the trait. Since there is no other
mutable visit, this seems acceptable.
The "const" visitors never use "will_visit_fields_of()" or
"did_visit_fields_of()", so remove them (though this is debatable).
Like in the C++ implementation, the AST nodes themselves are largely defined
via macros. Union fields like "Statement" and "ArgumentOrRedirection"
do currently not use macros but may in future.
This commit also introduces a precedent for a type that is defined in one
CXX bridge and used in another one - "ParseErrorList". To make this work
we need to manually define "ExternType".
There is one annoyance with CXX: functions that take explicit lifetime
parameters require to be marked as unsafe. This makes little sense
because functions that return `&Foo` with implicit lifetime can be
misused the same way on the C++ side.
One notable change is that we cannot directly port "find_block_open_keyword()"
(which is used to compute an error) because it relies on the stack of visited
nodes. We cannot modify a stack of node references while we do the "mut"
walk. Happily, an idiomatic solution is easy: we can tell the AST visitor
to backtrack to the parent node and create the error there.
Since "node_t::accept_base" is no longer a template we don't need the
"node_visitation_t" trampoline anymore.
The added copying at the FFI boundary makes things slower (memcpy dominates
the profile) but it's not unusable, which is good news:
$ hyperfine ./fish.{old,new}" -c 'source ../share/completions/git.fish'"
Benchmark 1: ./fish.old -c 'source ../share/completions/git.fish'
Time (mean ± σ): 195.5 ms ± 2.9 ms [User: 190.1 ms, System: 4.4 ms]
Range (min … max): 193.2 ms … 205.1 ms 15 runs
Benchmark 2: ./fish.new -c 'source ../share/completions/git.fish'
Time (mean ± σ): 677.5 ms ± 62.0 ms [User: 665.4 ms, System: 10.0 ms]
Range (min … max): 611.7 ms … 805.5 ms 10 runs
Summary
'./fish.old -c 'source ../share/completions/git.fish'' ran
3.47 ± 0.32 times faster than './fish.new -c 'source ../share/completions/git.fish''
Leftovers:
- Enum variants are still snakecase; I didn't get around to changing this yet.
- "ast_type_to_string()" still returns a snakecase name. This could be
changed since it's not user visible.
2023-04-02 14:42:59 +00:00
|
|
|
errored = parse_util_detect_errors(*ast, cmd_wcs, &*errors);
|
2021-07-27 15:44:02 +00:00
|
|
|
}
|
|
|
|
if (!errored) {
|
|
|
|
// Construct a parsed source ref.
|
|
|
|
// Be careful to transfer ownership, this could be a very large string.
|
Port AST to Rust
The translation is fairly direct though it adds some duplication, for example
there are multiple "match" statements that mimic function overloading.
Rust has no overloading, and we cannot have generic methods in the Node trait
(due to a Rust limitation, the error is like "cannot be made into an object")
so we include the type name in method names.
Give clients like "indent_visitor_t" a Rust companion ("IndentVisitor")
that takes care of the AST traversal while the AST consumption remains
in C++ for now. In future, "IndentVisitor" should absorb the entirety of
"indent_visitor_t". This pattern requires that "fish_indent" be exposed
includable header to the CXX bridge.
Alternatively, we could define FFI wrappers for recursive AST traversal.
Rust requires we separate the AST visitors for "mut" and "const"
scenarios. Take this opportunity to concretize both visitors:
The only client that requires mutable access is the populator. To match the
structure of the C++ populator which makes heavy use of function overloading,
we need to add a bunch of functions to the trait. Since there is no other
mutable visit, this seems acceptable.
The "const" visitors never use "will_visit_fields_of()" or
"did_visit_fields_of()", so remove them (though this is debatable).
Like in the C++ implementation, the AST nodes themselves are largely defined
via macros. Union fields like "Statement" and "ArgumentOrRedirection"
do currently not use macros but may in future.
This commit also introduces a precedent for a type that is defined in one
CXX bridge and used in another one - "ParseErrorList". To make this work
we need to manually define "ExternType".
There is one annoyance with CXX: functions that take explicit lifetime
parameters require to be marked as unsafe. This makes little sense
because functions that return `&Foo` with implicit lifetime can be
misused the same way on the C++ side.
One notable change is that we cannot directly port "find_block_open_keyword()"
(which is used to compute an error) because it relies on the stack of visited
nodes. We cannot modify a stack of node references while we do the "mut"
walk. Happily, an idiomatic solution is easy: we can tell the AST visitor
to backtrack to the parent node and create the error there.
Since "node_t::accept_base" is no longer a template we don't need the
"node_visitation_t" trampoline anymore.
The added copying at the FFI boundary makes things slower (memcpy dominates
the profile) but it's not unusable, which is good news:
$ hyperfine ./fish.{old,new}" -c 'source ../share/completions/git.fish'"
Benchmark 1: ./fish.old -c 'source ../share/completions/git.fish'
Time (mean ± σ): 195.5 ms ± 2.9 ms [User: 190.1 ms, System: 4.4 ms]
Range (min … max): 193.2 ms … 205.1 ms 15 runs
Benchmark 2: ./fish.new -c 'source ../share/completions/git.fish'
Time (mean ± σ): 677.5 ms ± 62.0 ms [User: 665.4 ms, System: 10.0 ms]
Range (min … max): 611.7 ms … 805.5 ms 10 runs
Summary
'./fish.old -c 'source ../share/completions/git.fish'' ran
3.47 ± 0.32 times faster than './fish.new -c 'source ../share/completions/git.fish''
Leftovers:
- Enum variants are still snakecase; I didn't get around to changing this yet.
- "ast_type_to_string()" still returns a snakecase name. This could be
changed since it's not user visible.
2023-04-02 14:42:59 +00:00
|
|
|
auto ps = new_parsed_source_ref(cmd_wcs, *ast);
|
2023-04-19 20:25:50 +00:00
|
|
|
parser.eval_parsed_source(*ps, io, {}, block_type_t::top);
|
2021-07-27 15:44:02 +00:00
|
|
|
} else {
|
|
|
|
wcstring sb;
|
2023-02-05 08:35:06 +00:00
|
|
|
parser.get_backtrace(cmd_wcs, *errors, sb);
|
2021-07-27 15:44:02 +00:00
|
|
|
std::fwprintf(stderr, L"%ls", sb.c_str());
|
|
|
|
}
|
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 14:21:00 +00:00
|
|
|
}
|
|
|
|
|
2019-12-23 12:49:40 +00:00
|
|
|
return 0;
|
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 14:21:00 +00:00
|
|
|
}
|
|
|
|
|
2016-05-16 02:45:02 +00:00
|
|
|
/// Parse the argument list, return the index of the first non-flag arguments.
|
2018-11-28 14:07:58 +00:00
|
|
|
static int fish_parse_opt(int argc, char **argv, fish_cmd_opts_t *opts) {
|
2021-02-15 19:57:11 +00:00
|
|
|
static const char *const short_opts = "+hPilNnvc:C:p:d:f:D:o:";
|
2019-11-19 02:34:50 +00: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'},
|
2021-02-15 19:57:11 +00:00
|
|
|
{"no-config", no_argument, nullptr, 'N'},
|
2019-11-19 02:34:50 +00:00
|
|
|
{"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'},
|
2021-01-08 16:25:00 +00:00
|
|
|
{"profile-startup", required_argument, nullptr, 3},
|
2019-11-19 02:34:50 +00:00
|
|
|
{"private", no_argument, nullptr, 'P'},
|
|
|
|
{"help", no_argument, nullptr, 'h'},
|
|
|
|
{"version", no_argument, nullptr, 'v'},
|
2022-04-01 21:25:02 +00:00
|
|
|
{}};
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-03-03 23:36:17 +00:00
|
|
|
int opt;
|
2019-11-19 02:34:50 +00:00
|
|
|
while ((opt = getopt_long(argc, argv, short_opts, long_opts, nullptr)) != -1) {
|
2016-05-01 01:37:19 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 'c': {
|
2020-02-21 06:51:01 +00:00
|
|
|
opts->batch_cmds.emplace_back(optarg);
|
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 14:21:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'C': {
|
2020-02-21 06:51:01 +00:00
|
|
|
opts->postconfig_cmds.emplace_back(optarg);
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-05-01 01:37:19 +00:00
|
|
|
case 'd': {
|
2021-07-05 21:29:24 +00:00
|
|
|
activate_flog_categories_by_pattern(str2wcstring(optarg));
|
2023-01-14 22:56:24 +00:00
|
|
|
rust_activate_flog_categories_by_pattern(str2wcstring(optarg).c_str());
|
2020-05-15 22:54:26 +00:00
|
|
|
for (auto cat : get_flog_categories()) {
|
|
|
|
if (cat->enabled) {
|
2021-05-09 04:30:08 +00:00
|
|
|
std::fwprintf(stdout, L"Debug enabled for category: %ls\n", cat->name);
|
2020-05-15 22:54:26 +00:00
|
|
|
}
|
|
|
|
}
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2019-04-20 07:15:51 +00:00
|
|
|
case 'o': {
|
|
|
|
opts->debug_output = optarg;
|
|
|
|
break;
|
|
|
|
}
|
2018-04-24 21:32:06 +00:00
|
|
|
case 'f': {
|
|
|
|
opts->features = str2wcstring(optarg);
|
|
|
|
break;
|
|
|
|
}
|
2016-05-01 01:37:19 +00:00
|
|
|
case 'h': {
|
2020-02-21 06:51:01 +00:00
|
|
|
opts->batch_cmds.emplace_back("__fish_print_help fish");
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-05-01 01:37:19 +00:00
|
|
|
case 'i': {
|
2019-05-12 22:48:00 +00:00
|
|
|
opts->is_interactive_session = true;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-05-01 01:37:19 +00:00
|
|
|
case 'l': {
|
2019-05-12 22:48:00 +00:00
|
|
|
opts->is_login = true;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-02-15 19:57:11 +00:00
|
|
|
case 'N': {
|
|
|
|
opts->no_config = true;
|
2021-04-11 11:09:03 +00:00
|
|
|
// --no-config implies private mode, we won't be saving history
|
|
|
|
opts->enable_private_mode = true;
|
2021-02-15 19:57:11 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-05-01 01:37:19 +00:00
|
|
|
case 'n': {
|
2019-05-12 22:48:00 +00:00
|
|
|
opts->no_exec = true;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-04-10 20:58:29 +00:00
|
|
|
case 1: {
|
|
|
|
opts->print_rusage_self = true;
|
|
|
|
break;
|
|
|
|
}
|
2019-04-20 07:15:51 +00:00
|
|
|
case 2: {
|
|
|
|
auto cats = get_flog_categories();
|
|
|
|
// Compute width of longest name.
|
|
|
|
int name_width = 0;
|
2020-04-02 23:04:04 +00:00
|
|
|
for (auto cat : cats) {
|
2019-11-19 01:08:16 +00:00
|
|
|
name_width = std::max(name_width, static_cast<int>(wcslen(cat->name)));
|
2019-04-20 07:15:51 +00:00
|
|
|
}
|
|
|
|
// A little extra space.
|
|
|
|
name_width += 2;
|
2020-04-02 23:04:04 +00:00
|
|
|
for (auto cat : cats) {
|
2019-04-20 07:15:51 +00:00
|
|
|
// Negating the name width left-justifies.
|
|
|
|
printf("%*ls %ls\n", -name_width, cat->name, _(cat->description));
|
|
|
|
}
|
|
|
|
exit(0);
|
|
|
|
}
|
2016-05-01 01:37:19 +00:00
|
|
|
case 'p': {
|
2021-01-08 16:25:00 +00:00
|
|
|
// "--profile" - this does not activate profiling right away,
|
|
|
|
// rather it's done after startup is finished.
|
2020-07-22 22:35:14 +00:00
|
|
|
opts->profile_output = optarg;
|
2021-01-08 16:25:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3: {
|
|
|
|
// With "--profile-startup" we immediately turn profiling on.
|
|
|
|
opts->profile_startup_output = optarg;
|
2014-02-09 22:04:43 +00:00
|
|
|
g_profiling_active = true;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-09-19 23:56:08 +00:00
|
|
|
case 'P': {
|
2020-02-08 20:23:39 +00:00
|
|
|
opts->enable_private_mode = true;
|
2018-09-19 23:56:08 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-05-01 01:37:19 +00:00
|
|
|
case 'v': {
|
2019-03-12 21:06:01 +00:00
|
|
|
std::fwprintf(stdout, _(L"%s, version %s\n"), PACKAGE_NAME, get_fish_version());
|
2016-03-23 18:42:17 +00:00
|
|
|
exit(0);
|
2012-11-19 08:31:03 +00:00
|
|
|
}
|
2016-05-16 02:45:02 +00:00
|
|
|
case 'D': {
|
2020-11-15 10:32:52 +00:00
|
|
|
// TODO: Option is currently useless.
|
|
|
|
// Either remove it or make it work with FLOG.
|
2016-05-16 02:45:02 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-05-01 01:37:19 +00:00
|
|
|
default: {
|
2016-03-03 23:36:17 +00:00
|
|
|
// We assume getopt_long() has already emitted a diagnostic msg.
|
2016-03-23 18:42:17 +00:00
|
|
|
exit(1);
|
2012-11-19 08:31:03 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-19 04:05:00 +00:00
|
|
|
// If our command name begins with a dash that implies we're a login shell.
|
2019-05-12 22:48:00 +00:00
|
|
|
opts->is_login |= argv[0][0] == '-';
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2015-12-19 04:05:00 +00: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 00:56:46 +00:00
|
|
|
if (opts->batch_cmds.empty() && optind == argc && isatty(STDIN_FILENO)) {
|
2020-12-06 21:40:45 +00:00
|
|
|
set_interactive_session(true);
|
2013-10-26 22:24:49 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2015-12-19 04:05:00 +00:00
|
|
|
return optind;
|
2006-10-25 20:54:43 +00:00
|
|
|
}
|
|
|
|
|
2018-11-28 14:07:58 +00:00
|
|
|
int main(int argc, char **argv) {
|
2016-05-01 01:37:19 +00:00
|
|
|
int res = 1;
|
|
|
|
int my_optind = 0;
|
2006-10-25 20:54:43 +00:00
|
|
|
|
2018-11-28 14:08:24 +00:00
|
|
|
program_name = L"fish";
|
2023-01-14 22:56:24 +00:00
|
|
|
rust_init();
|
2017-05-03 03:55:41 +00:00
|
|
|
signal_unblock_all();
|
2022-03-25 04:43:58 +00:00
|
|
|
|
2016-06-04 02:05:13 +00:00
|
|
|
setlocale(LC_ALL, "");
|
2006-10-25 20:54:43 +00:00
|
|
|
|
2019-11-19 02:34:50 +00:00
|
|
|
const char *dummy_argv[2] = {"fish", nullptr};
|
2018-11-28 14:08:24 +00:00
|
|
|
if (!argv[0]) {
|
2020-04-08 23:56:59 +00:00
|
|
|
argv = const_cast<char **>(dummy_argv); //!OCLINT(parameter reassignment)
|
|
|
|
argc = 1; //!OCLINT(parameter reassignment)
|
2018-11-28 14:08:24 +00:00
|
|
|
}
|
2020-09-28 15:10:40 +00:00
|
|
|
|
|
|
|
// Enable debug categories set in FISH_DEBUG.
|
|
|
|
// This is in *addition* to the ones given via --debug.
|
|
|
|
if (const char *debug_categories = getenv("FISH_DEBUG")) {
|
|
|
|
activate_flog_categories_by_pattern(str2wcstring(debug_categories));
|
|
|
|
}
|
|
|
|
|
2019-05-12 22:48:00 +00: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 14:21:00 +00:00
|
|
|
my_optind = fish_parse_opt(argc, argv, &opts);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2019-04-20 07:15:51 +00:00
|
|
|
// Direct any debug output right away.
|
2020-09-28 15:10:40 +00:00
|
|
|
// --debug-output takes precedence, otherwise $FISH_DEBUG_OUTPUT is used.
|
|
|
|
if (opts.debug_output.empty()) {
|
|
|
|
const char *var = getenv("FISH_DEBUG_OUTPUT");
|
|
|
|
if (var) opts.debug_output = var;
|
|
|
|
}
|
|
|
|
|
2019-04-20 07:15:51 +00:00
|
|
|
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 01:37:19 +00:00
|
|
|
// No-exec is prohibited when in interactive mode.
|
2019-05-12 22:48:00 +00:00
|
|
|
if (opts.is_interactive_session && opts.no_exec) {
|
2020-01-19 12:38:47 +00:00
|
|
|
FLOGF(warning, _(L"Can not use the no-execute mode when running an interactive session"));
|
2019-05-12 22:48:00 +00:00
|
|
|
opts.no_exec = false;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2013-10-26 22:27:39 +00:00
|
|
|
|
2019-05-12 22:48:00 +00:00
|
|
|
// Apply our options.
|
|
|
|
if (opts.is_login) mark_login();
|
|
|
|
if (opts.no_exec) mark_no_exec();
|
2020-12-06 21:40:45 +00:00
|
|
|
if (opts.is_interactive_session) set_interactive_session(true);
|
2020-02-08 20:23:39 +00:00
|
|
|
if (opts.enable_private_mode) start_private_mode(env_stack_t::globals());
|
2019-05-12 22:48:00 +00:00
|
|
|
|
2016-05-01 01:37:19 +00:00
|
|
|
// Only save (and therefore restore) the fg process group if we are interactive. See issues
|
|
|
|
// #197 and #1002.
|
2020-12-06 21:40:45 +00:00
|
|
|
if (is_interactive_session()) {
|
2013-10-26 22:22:20 +00:00
|
|
|
save_term_foreground_process_group();
|
|
|
|
}
|
|
|
|
|
2021-02-15 17:59:54 +00:00
|
|
|
struct config_paths_t paths;
|
|
|
|
// If we're not executing, there's no need to find the config.
|
2021-04-11 09:52:30 +00:00
|
|
|
if (!opts.no_exec) {
|
2021-02-15 17:59:54 +00:00
|
|
|
paths = determine_config_directory_paths(argv[0]);
|
2021-05-01 17:43:31 +00:00
|
|
|
env_init(&paths, /* do uvars */ !opts.no_config, /* default paths */ opts.no_config);
|
2021-02-15 17:59:54 +00:00
|
|
|
}
|
2020-02-08 20:23:39 +00:00
|
|
|
|
2018-04-24 21:32:06 +00:00
|
|
|
// Set features early in case other initialization depends on them.
|
2018-04-24 23:48:13 +00: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 01:04:30 +00:00
|
|
|
if (auto features_var = env_stack_t::globals().get(L"fish_features")) {
|
2018-04-24 23:48:13 +00:00
|
|
|
for (const wcstring &s : features_var->as_list()) {
|
2023-02-03 15:34:29 +00:00
|
|
|
mutable_fish_features()->set_from_string(s.c_str());
|
2018-04-24 23:48:13 +00:00
|
|
|
}
|
|
|
|
}
|
2023-02-03 15:34:29 +00:00
|
|
|
mutable_fish_features()->set_from_string(opts.features.c_str());
|
2012-11-19 00:30:30 +00:00
|
|
|
proc_init();
|
2017-02-16 04:09:26 +00:00
|
|
|
misc_init();
|
2012-11-19 00:30:30 +00:00
|
|
|
reader_init();
|
2006-03-10 13:38:09 +00:00
|
|
|
|
2012-01-23 05:40:08 +00:00
|
|
|
parser_t &parser = parser_t::principal_parser();
|
2022-03-28 01:59:34 +00:00
|
|
|
parser.set_syncs_uvars(!opts.no_config);
|
2012-01-23 05:40:08 +00:00
|
|
|
|
2021-02-15 19:57:11 +00:00
|
|
|
if (!opts.no_exec && !opts.no_config) {
|
2021-02-15 17:59:54 +00:00
|
|
|
read_init(parser, paths);
|
|
|
|
}
|
2022-03-17 18:02:12 +00:00
|
|
|
|
2022-06-13 15:17:29 +00:00
|
|
|
if (is_interactive_session() && opts.no_config && !opts.no_exec) {
|
2022-03-17 18:02:12 +00:00
|
|
|
// If we have no config, we default to the default key bindings.
|
|
|
|
parser.vars().set_one(L"fish_key_bindings", ENV_UNEXPORT, L"fish_default_key_bindings");
|
|
|
|
if (function_exists(L"fish_default_key_bindings", parser)) {
|
2022-06-20 19:55:33 +00:00
|
|
|
run_command_list(parser, {"fish_default_key_bindings"}, {});
|
2022-03-17 18:02:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-06 08:24:54 +00:00
|
|
|
// Re-read the terminal modes after config, it might have changed them.
|
|
|
|
term_copy_modes();
|
|
|
|
|
2020-04-03 19:24:31 +00:00
|
|
|
// Stomp the exit status of any initialization commands (issue #635).
|
|
|
|
parser.set_last_statuses(statuses_t::just(STATUS_CMD_OK));
|
2013-05-05 09:33:17 +00:00
|
|
|
|
2021-01-08 16:25:00 +00:00
|
|
|
// If we're profiling startup to a separate file, write it now.
|
2021-04-21 20:31:58 +00:00
|
|
|
if (!opts.profile_startup_output.empty() &&
|
|
|
|
opts.profile_startup_output != opts.profile_output) {
|
2021-01-08 16:25:00 +00:00
|
|
|
parser.emit_profiling(opts.profile_startup_output.c_str());
|
|
|
|
|
|
|
|
// If we are profiling both, ensure the startup data only
|
|
|
|
// ends up in the startup file.
|
|
|
|
parser.clear_profiling();
|
|
|
|
}
|
|
|
|
|
|
|
|
g_profiling_active = !opts.profile_output.empty();
|
|
|
|
|
2020-04-03 19:24:31 +00:00
|
|
|
// Run post-config commands specified as arguments, if any.
|
|
|
|
if (!opts.postconfig_cmds.empty()) {
|
2022-06-20 19:55:33 +00:00
|
|
|
res = run_command_list(parser, opts.postconfig_cmds, {});
|
2020-04-03 19:24:31 +00: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 14:21:00 +00:00
|
|
|
|
2022-06-20 20:26:48 +00:00
|
|
|
// Clear signals in case we were interrupted (#9024).
|
|
|
|
signal_clear_cancel();
|
|
|
|
|
2020-04-03 19:24:31 +00:00
|
|
|
if (!opts.batch_cmds.empty()) {
|
|
|
|
// Run the commands specified as arguments, if any.
|
|
|
|
if (get_login()) {
|
|
|
|
// 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);
|
|
|
|
}
|
2020-09-26 12:45:51 +00:00
|
|
|
|
|
|
|
// Pass additional args as $argv.
|
|
|
|
// Note that we *don't* support setting argv[0]/$0, unlike e.g. bash.
|
2023-04-18 22:19:10 +00:00
|
|
|
std::vector<wcstring> list;
|
2020-09-26 12:45:51 +00:00
|
|
|
for (char **ptr = argv + my_optind; *ptr; ptr++) {
|
|
|
|
list.push_back(str2wcstring(*ptr));
|
|
|
|
}
|
|
|
|
parser.vars().set(L"argv", ENV_DEFAULT, std::move(list));
|
2022-06-20 19:55:33 +00:00
|
|
|
res = run_command_list(parser, opts.batch_cmds, {});
|
2020-08-15 21:41:11 +00:00
|
|
|
parser.libdata().exit_current_script = false;
|
2020-04-03 19:24:31 +00:00
|
|
|
} else if (my_optind == argc) {
|
|
|
|
// Implicitly interactive mode.
|
2021-01-01 20:15:09 +00:00
|
|
|
if (opts.no_exec && isatty(STDIN_FILENO)) {
|
|
|
|
FLOGF(error, L"no-execute mode enabled and no script given. Exiting");
|
|
|
|
return EXIT_FAILURE; // above line should always exit
|
|
|
|
}
|
2020-04-03 19:24:31 +00:00
|
|
|
res = reader_read(parser, STDIN_FILENO, {});
|
|
|
|
} else {
|
|
|
|
const char *file = *(argv + (my_optind++));
|
|
|
|
autoclose_fd_t fd(open_cloexec(file, O_RDONLY));
|
|
|
|
if (!fd.valid()) {
|
2021-11-20 16:40:47 +00:00
|
|
|
FLOGF(error, _(L"Error reading script file '%s':"), file);
|
|
|
|
perror("error");
|
2016-05-01 01:37:19 +00:00
|
|
|
} else {
|
2023-04-18 22:19:10 +00:00
|
|
|
std::vector<wcstring> list;
|
2020-04-03 19:24:31 +00:00
|
|
|
for (char **ptr = argv + my_optind; *ptr; ptr++) {
|
|
|
|
list.push_back(str2wcstring(*ptr));
|
|
|
|
}
|
|
|
|
parser.vars().set(L"argv", ENV_DEFAULT, std::move(list));
|
|
|
|
|
|
|
|
auto &ld = parser.libdata();
|
2022-08-13 19:15:23 +00:00
|
|
|
filename_ref_t rel_filename = std::make_shared<wcstring>(str2wcstring(file));
|
|
|
|
scoped_push<filename_ref_t> filename_push{&ld.current_filename, rel_filename};
|
2020-04-03 19:24:31 +00:00
|
|
|
res = reader_read(parser, fd.fd(), {});
|
|
|
|
if (res) {
|
2022-08-13 19:15:23 +00:00
|
|
|
FLOGF(warning, _(L"Error while reading file %ls\n"), rel_filename->c_str());
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-12 21:00:44 +00:00
|
|
|
int exit_status = res ? STATUS_CMD_UNKNOWN : parser.get_last_status();
|
2023-02-11 20:31:08 +00:00
|
|
|
event_fire(parser, *new_event_process_exit(getpid(), exit_status));
|
2019-01-04 04:20:43 +00:00
|
|
|
|
|
|
|
// Trigger any exit handlers.
|
2022-05-09 00:07:34 +00:00
|
|
|
event_fire_generic(parser, L"fish_exit", {to_string(exit_status)});
|
2013-10-26 22:27:39 +00:00
|
|
|
|
2013-10-26 22:22:20 +00:00
|
|
|
restore_term_mode();
|
2020-05-31 21:11:39 +00:00
|
|
|
restore_term_foreground_process_group_for_exit();
|
2014-03-31 17:01:39 +00:00
|
|
|
|
2021-01-08 16:25:00 +00:00
|
|
|
if (!opts.profile_output.empty()) {
|
2020-07-22 22:35:14 +00:00
|
|
|
parser.emit_profiling(opts.profile_output.c_str());
|
2014-02-09 22:04:43 +00:00
|
|
|
}
|
2014-03-31 17:01:39 +00:00
|
|
|
|
2018-09-29 00:20:54 +00:00
|
|
|
history_save_all();
|
2019-04-10 20:58:29 +00:00
|
|
|
if (opts.print_rusage_self) {
|
|
|
|
print_rusage_self(stderr);
|
|
|
|
}
|
2019-04-20 07:15:51 +00:00
|
|
|
if (debug_output) {
|
|
|
|
fclose(debug_output);
|
|
|
|
}
|
2023-04-29 17:07:59 +00:00
|
|
|
asan_maybe_exit(exit_status);
|
2014-10-28 00:23:08 +00:00
|
|
|
exit_without_destructors(exit_status);
|
2016-05-01 01:37:19 +00:00
|
|
|
return EXIT_FAILURE; // above line should always exit
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|