mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 15:22:43 +00:00 
			
		
		
		
	 5e1499d104
			
		
	
	
		5e1499d104
		
	
	
	
	
		
			
			This commit un-deprecates DeprecatedString, and repurposes it as a byte
string.
As the null state has already been removed, there are no other
particularly hairy blockers in repurposing this type as a byte string
(what it _really_ is).
This commit is auto-generated:
  $ xs=$(ack -l \bDeprecatedString\b\|deprecated_string AK Userland \
    Meta Ports Ladybird Tests Kernel)
  $ perl -pie 's/\bDeprecatedString\b/ByteString/g;
    s/deprecated_string/byte_string/g' $xs
  $ clang-format --style=file -i \
    $(git diff --name-only | grep \.cpp\|\.h)
  $ gn format $(git ls-files '*.gn' '*.gni')
		
	
			
		
			
				
	
	
		
			615 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			615 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2020-2022, the SerenityOS developers.
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #include "Job.h"
 | |
| #include "Parser.h"
 | |
| #include <AK/Array.h>
 | |
| #include <AK/ByteString.h>
 | |
| #include <AK/CircularQueue.h>
 | |
| #include <AK/HashMap.h>
 | |
| #include <AK/IntrusiveList.h>
 | |
| #include <AK/StackInfo.h>
 | |
| #include <AK/StringBuilder.h>
 | |
| #include <AK/StringView.h>
 | |
| #include <AK/Types.h>
 | |
| #include <AK/Vector.h>
 | |
| #include <LibCore/EventReceiver.h>
 | |
| #include <LibCore/Notifier.h>
 | |
| #include <LibLine/Editor.h>
 | |
| #include <LibMain/Main.h>
 | |
| #include <termios.h>
 | |
| 
 | |
| #define ENUMERATE_SHELL_BUILTINS()                           \
 | |
|     __ENUMERATE_SHELL_BUILTIN(alias, InAllModes)             \
 | |
|     __ENUMERATE_SHELL_BUILTIN(where, InAllModes)             \
 | |
|     __ENUMERATE_SHELL_BUILTIN(cd, InAllModes)                \
 | |
|     __ENUMERATE_SHELL_BUILTIN(cdh, InAllModes)               \
 | |
|     __ENUMERATE_SHELL_BUILTIN(pwd, InAllModes)               \
 | |
|     __ENUMERATE_SHELL_BUILTIN(type, InAllModes)              \
 | |
|     __ENUMERATE_SHELL_BUILTIN(exec, InAllModes)              \
 | |
|     __ENUMERATE_SHELL_BUILTIN(eval, OnlyInPOSIXMode)         \
 | |
|     __ENUMERATE_SHELL_BUILTIN(exit, InAllModes)              \
 | |
|     __ENUMERATE_SHELL_BUILTIN(export, InAllModes)            \
 | |
|     __ENUMERATE_SHELL_BUILTIN(glob, InAllModes)              \
 | |
|     __ENUMERATE_SHELL_BUILTIN(unalias, InAllModes)           \
 | |
|     __ENUMERATE_SHELL_BUILTIN(unset, InAllModes)             \
 | |
|     __ENUMERATE_SHELL_BUILTIN(set, InAllModes)               \
 | |
|     __ENUMERATE_SHELL_BUILTIN(history, InAllModes)           \
 | |
|     __ENUMERATE_SHELL_BUILTIN(umask, InAllModes)             \
 | |
|     __ENUMERATE_SHELL_BUILTIN(not, InAllModes)               \
 | |
|     __ENUMERATE_SHELL_BUILTIN(dirs, InAllModes)              \
 | |
|     __ENUMERATE_SHELL_BUILTIN(pushd, InAllModes)             \
 | |
|     __ENUMERATE_SHELL_BUILTIN(popd, InAllModes)              \
 | |
|     __ENUMERATE_SHELL_BUILTIN(setopt, InAllModes)            \
 | |
|     __ENUMERATE_SHELL_BUILTIN(shift, InAllModes)             \
 | |
|     __ENUMERATE_SHELL_BUILTIN(source, InAllModes)            \
 | |
|     __ENUMERATE_SHELL_BUILTIN(time, InAllModes)              \
 | |
|     __ENUMERATE_SHELL_BUILTIN(jobs, InAllModes)              \
 | |
|     __ENUMERATE_SHELL_BUILTIN(disown, InAllModes)            \
 | |
|     __ENUMERATE_SHELL_BUILTIN(fg, InAllModes)                \
 | |
|     __ENUMERATE_SHELL_BUILTIN(bg, InAllModes)                \
 | |
|     __ENUMERATE_SHELL_BUILTIN(wait, InAllModes)              \
 | |
|     __ENUMERATE_SHELL_BUILTIN(dump, InAllModes)              \
 | |
|     __ENUMERATE_SHELL_BUILTIN(kill, InAllModes)              \
 | |
|     __ENUMERATE_SHELL_BUILTIN(reset, InAllModes)             \
 | |
|     __ENUMERATE_SHELL_BUILTIN(noop, InAllModes)              \
 | |
|     __ENUMERATE_SHELL_BUILTIN(break, OnlyInPOSIXMode)        \
 | |
|     __ENUMERATE_SHELL_BUILTIN(continue, OnlyInPOSIXMode)     \
 | |
|     __ENUMERATE_SHELL_BUILTIN(return, InAllModes)            \
 | |
|     __ENUMERATE_SHELL_BUILTIN(read, OnlyInPOSIXMode)         \
 | |
|     __ENUMERATE_SHELL_BUILTIN(run_with_env, OnlyInPOSIXMode) \
 | |
|     __ENUMERATE_SHELL_BUILTIN(argsparser_parse, InAllModes)  \
 | |
|     __ENUMERATE_SHELL_BUILTIN(in_parallel, InAllModes)       \
 | |
|     __ENUMERATE_SHELL_BUILTIN(shell_set_active_prompt, InAllModes)
 | |
| 
 | |
| #define ENUMERATE_SHELL_OPTIONS()                                                                                    \
 | |
|     __ENUMERATE_SHELL_OPTION(inline_exec_keep_empty_segments, false, "Keep empty segments in inline execute $(...)") \
 | |
|     __ENUMERATE_SHELL_OPTION(verbose, false, "Announce every command that is about to be executed")                  \
 | |
|     __ENUMERATE_SHELL_OPTION(invoke_program_for_autocomplete, false, "Attempt to use the program being completed itself for autocompletion via --complete")
 | |
| 
 | |
| #define ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS()                          \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(concat_lists)                 \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(length)                       \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(length_across)                \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(remove_suffix)                \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(remove_prefix)                \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(regex_replace)                \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(filter_glob)                  \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(split)                        \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(join)                         \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(value_or_default)             \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(assign_default)               \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(error_if_empty)               \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(null_or_alternative)          \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(defined_value_or_default)     \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(assign_defined_default)       \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(error_if_unset)               \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(null_if_unset_or_alternative) \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(length_of_variable)           \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(reexpand)                     \
 | |
|     __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(math)
 | |
| 
 | |
| namespace Shell {
 | |
| 
 | |
| class Shell;
 | |
| 
 | |
| enum class POSIXModeRequirement {
 | |
|     OnlyInPOSIXMode,
 | |
|     InAllModes,
 | |
| };
 | |
| 
 | |
| class Shell : public Core::EventReceiver {
 | |
|     C_OBJECT(Shell);
 | |
| 
 | |
| public:
 | |
|     constexpr static auto local_init_file_path = "~/.shellrc";
 | |
|     constexpr static auto global_init_file_path = "/etc/shellrc";
 | |
|     constexpr static auto local_posix_init_file_path = "~/.posixshrc";
 | |
|     constexpr static auto global_posix_init_file_path = "/etc/posixshrc";
 | |
| 
 | |
|     bool should_format_live() const { return m_should_format_live; }
 | |
|     void set_live_formatting(bool value) { m_should_format_live = value; }
 | |
| 
 | |
|     void setup_signals();
 | |
|     void setup_keybinds();
 | |
| 
 | |
|     struct SourcePosition {
 | |
|         Optional<ByteString> source_file;
 | |
|         ByteString literal_source_text;
 | |
|         Optional<AST::Position> position;
 | |
|     };
 | |
| 
 | |
|     struct RunnablePath {
 | |
|         enum class Kind {
 | |
|             Builtin,
 | |
|             Function,
 | |
|             Alias,
 | |
|             Executable,
 | |
|         };
 | |
| 
 | |
|         Kind kind;
 | |
|         ByteString path;
 | |
| 
 | |
|         bool operator<(RunnablePath const& other) const
 | |
|         {
 | |
|             return path < other.path;
 | |
|         }
 | |
| 
 | |
|         bool operator==(RunnablePath const&) const = default;
 | |
|     };
 | |
| 
 | |
|     struct RunnablePathComparator {
 | |
|         int operator()(RunnablePath const& lhs, RunnablePath const& rhs)
 | |
|         {
 | |
|             if (lhs.path > rhs.path)
 | |
|                 return 1;
 | |
| 
 | |
|             if (lhs.path < rhs.path)
 | |
|                 return -1;
 | |
| 
 | |
|             return 0;
 | |
|         }
 | |
| 
 | |
|         int operator()(StringView lhs, RunnablePath const& rhs)
 | |
|         {
 | |
|             if (lhs > rhs.path)
 | |
|                 return 1;
 | |
| 
 | |
|             if (lhs < rhs.path)
 | |
|                 return -1;
 | |
| 
 | |
|             return 0;
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     int run_command(StringView, Optional<SourcePosition> = {});
 | |
|     Optional<RunnablePath> runnable_path_for(StringView);
 | |
|     Optional<ByteString> help_path_for(Vector<RunnablePath> visited, RunnablePath const& runnable_path);
 | |
|     ErrorOr<RefPtr<Job>> run_command(const AST::Command&);
 | |
|     Vector<NonnullRefPtr<Job>> run_commands(Vector<AST::Command>&);
 | |
|     bool run_file(ByteString const&, bool explicitly_invoked = true);
 | |
|     ErrorOr<bool> run_builtin(const AST::Command&, Vector<NonnullRefPtr<AST::Rewiring>> const&, int& retval);
 | |
|     bool has_builtin(StringView) const;
 | |
|     ErrorOr<RefPtr<AST::Node>> run_immediate_function(StringView name, AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const&);
 | |
|     static bool has_immediate_function(StringView);
 | |
|     void block_on_job(RefPtr<Job>);
 | |
|     void block_on_pipeline(RefPtr<AST::Pipeline>);
 | |
|     ByteString prompt() const;
 | |
| 
 | |
|     static ByteString expand_tilde(StringView expression);
 | |
|     static ErrorOr<Vector<ByteString>> expand_globs(StringView path, StringView base);
 | |
|     static Vector<ByteString> expand_globs(Vector<StringView> path_segments, StringView base);
 | |
|     ErrorOr<Vector<AST::Command>> expand_aliases(Vector<AST::Command>);
 | |
|     ByteString resolve_path(ByteString) const;
 | |
|     Optional<ByteString> resolve_alias(StringView) const;
 | |
| 
 | |
|     static bool has_history_event(StringView);
 | |
| 
 | |
|     ErrorOr<RefPtr<AST::Value const>> get_argument(size_t) const;
 | |
|     ErrorOr<RefPtr<AST::Value const>> look_up_local_variable(StringView) const;
 | |
|     ErrorOr<ByteString> local_variable_or(StringView, ByteString const&) const;
 | |
|     void set_local_variable(ByteString const&, RefPtr<AST::Value>, bool only_in_current_frame = false);
 | |
|     void unset_local_variable(StringView, bool only_in_current_frame = false);
 | |
| 
 | |
|     void define_function(ByteString name, Vector<ByteString> argnames, RefPtr<AST::Node> body);
 | |
|     bool has_function(StringView);
 | |
|     bool invoke_function(const AST::Command&, int& retval);
 | |
| 
 | |
|     ByteString format(StringView, ssize_t& cursor) const;
 | |
| 
 | |
|     RefPtr<Line::Editor> editor() const { return m_editor; }
 | |
| 
 | |
|     enum class LocalFrameKind {
 | |
|         FunctionOrGlobal,
 | |
|         Block,
 | |
|     };
 | |
|     struct LocalFrame {
 | |
|         LocalFrame(ByteString name, HashMap<ByteString, RefPtr<AST::Value>> variables, LocalFrameKind kind = LocalFrameKind::Block)
 | |
|             : name(move(name))
 | |
|             , local_variables(move(variables))
 | |
|             , is_function_frame(kind == LocalFrameKind::FunctionOrGlobal)
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         ByteString name;
 | |
|         HashMap<ByteString, RefPtr<AST::Value>> local_variables;
 | |
|         bool is_function_frame;
 | |
|     };
 | |
| 
 | |
|     struct Frame {
 | |
|         Frame(Vector<NonnullOwnPtr<LocalFrame>>& frames, LocalFrame const& frame)
 | |
|             : frames(frames)
 | |
|             , frame(frame)
 | |
|         {
 | |
|         }
 | |
|         ~Frame();
 | |
| 
 | |
|         void leak_frame() { should_destroy_frame = false; }
 | |
| 
 | |
|     private:
 | |
|         Vector<NonnullOwnPtr<LocalFrame>>& frames;
 | |
|         LocalFrame const& frame;
 | |
|         bool should_destroy_frame { true };
 | |
|     };
 | |
| 
 | |
|     [[nodiscard]] Frame push_frame(ByteString name, LocalFrameKind = LocalFrameKind::Block);
 | |
|     void pop_frame();
 | |
| 
 | |
|     struct Promise {
 | |
|         struct Data {
 | |
|             struct Unveil {
 | |
|                 ByteString path;
 | |
|                 ByteString access;
 | |
|             };
 | |
|             ByteString exec_promises;
 | |
|             Vector<Unveil> unveils;
 | |
|         } data;
 | |
| 
 | |
|         IntrusiveListNode<Promise> node;
 | |
|         using List = IntrusiveList<&Promise::node>;
 | |
|     };
 | |
| 
 | |
|     struct ScopedPromise {
 | |
|         ScopedPromise(Promise::List& promises, Promise&& promise)
 | |
|             : promises(promises)
 | |
|             , promise(move(promise))
 | |
|         {
 | |
|             promises.append(this->promise);
 | |
|         }
 | |
| 
 | |
|         ~ScopedPromise()
 | |
|         {
 | |
|             promises.remove(promise);
 | |
|         }
 | |
| 
 | |
|         Promise::List& promises;
 | |
|         Promise promise;
 | |
|     };
 | |
|     [[nodiscard]] ScopedPromise promise(Promise::Data data)
 | |
|     {
 | |
|         return { m_active_promises, { move(data), {} } };
 | |
|     }
 | |
| 
 | |
|     enum class EscapeMode {
 | |
|         Bareword,
 | |
|         SingleQuotedString,
 | |
|         DoubleQuotedString,
 | |
|     };
 | |
|     static ByteString escape_token_for_double_quotes(StringView token);
 | |
|     static ByteString escape_token_for_single_quotes(StringView token);
 | |
|     static ByteString escape_token(StringView token, EscapeMode = EscapeMode::Bareword);
 | |
|     static ByteString escape_token(Utf32View token, EscapeMode = EscapeMode::Bareword);
 | |
|     static ByteString unescape_token(StringView token);
 | |
|     enum class SpecialCharacterEscapeMode {
 | |
|         Untouched,
 | |
|         Escaped,
 | |
|         QuotedAsEscape,
 | |
|         QuotedAsHex,
 | |
|     };
 | |
|     static SpecialCharacterEscapeMode special_character_escape_mode(u32 c, EscapeMode);
 | |
| 
 | |
|     static bool is_glob(StringView);
 | |
| 
 | |
|     enum class ExecutableOnly {
 | |
|         Yes,
 | |
|         No
 | |
|     };
 | |
| 
 | |
|     ErrorOr<void> highlight(Line::Editor&) const;
 | |
|     Vector<Line::CompletionSuggestion> complete();
 | |
|     Vector<Line::CompletionSuggestion> complete(StringView);
 | |
|     Vector<Line::CompletionSuggestion> complete_program_name(StringView, size_t offset, EscapeMode = EscapeMode::Bareword);
 | |
|     Vector<Line::CompletionSuggestion> complete_variable(StringView, size_t offset);
 | |
|     Vector<Line::CompletionSuggestion> complete_user(StringView, size_t offset);
 | |
|     Vector<Line::CompletionSuggestion> complete_immediate_function_name(StringView, size_t offset);
 | |
| 
 | |
|     Vector<Line::CompletionSuggestion> complete_path(StringView base, StringView, size_t offset, ExecutableOnly executable_only, AST::Node const* command_node, AST::Node const*, EscapeMode = EscapeMode::Bareword);
 | |
|     Vector<Line::CompletionSuggestion> complete_option(StringView, StringView, size_t offset, AST::Node const* command_node, AST::Node const*);
 | |
|     ErrorOr<Vector<Line::CompletionSuggestion>> complete_via_program_itself(size_t offset, AST::Node const* command_node, AST::Node const*, EscapeMode escape_mode, StringView known_program_name);
 | |
| 
 | |
|     void restore_ios();
 | |
| 
 | |
|     u64 find_last_job_id() const;
 | |
|     Job* find_job(u64 id, bool is_pid = false);
 | |
|     Job* current_job() const { return m_current_job; }
 | |
|     void kill_job(Job const*, int sig);
 | |
| 
 | |
|     ByteString get_history_path();
 | |
|     void print_path(StringView path);
 | |
|     void cache_path();
 | |
| 
 | |
|     bool read_single_line();
 | |
| 
 | |
|     void notify_child_event();
 | |
| 
 | |
|     bool posix_mode() const { return m_in_posix_mode; }
 | |
| 
 | |
|     struct termios termios;
 | |
|     struct termios default_termios;
 | |
|     bool was_interrupted { false };
 | |
|     bool was_resized { false };
 | |
| 
 | |
|     ByteString cwd;
 | |
|     ByteString username;
 | |
|     ByteString home;
 | |
| 
 | |
|     constexpr static auto TTYNameSize = 32;
 | |
|     constexpr static auto HostNameSize = 64;
 | |
| 
 | |
|     char ttyname[TTYNameSize];
 | |
|     char hostname[HostNameSize];
 | |
| 
 | |
|     uid_t uid;
 | |
|     Optional<int> last_return_code;
 | |
|     Vector<ByteString> directory_stack;
 | |
|     CircularQueue<ByteString, 8> cd_history; // FIXME: have a configurable cd history length
 | |
|     HashMap<u64, NonnullRefPtr<Job>> jobs;
 | |
|     Vector<RunnablePath, 256> cached_path;
 | |
| 
 | |
|     ByteString current_script;
 | |
| 
 | |
|     enum ShellEventType {
 | |
|         ReadLine,
 | |
|     };
 | |
| 
 | |
|     enum class ShellError {
 | |
|         None,
 | |
|         InternalControlFlowBreak,
 | |
|         InternalControlFlowContinue,
 | |
|         InternalControlFlowReturn,
 | |
|         InternalControlFlowInterrupted,
 | |
|         InternalControlFlowKilled,
 | |
|         EvaluatedSyntaxError,
 | |
|         NonExhaustiveMatchRules,
 | |
|         InvalidGlobError,
 | |
|         InvalidSliceContentsError,
 | |
|         OpenFailure,
 | |
|         OutOfMemory,
 | |
|         LaunchError,
 | |
|         PipeFailure,
 | |
|         WriteFailure,
 | |
|     };
 | |
| 
 | |
|     void raise_error(ShellError kind, ByteString description, Optional<AST::Position> position = {})
 | |
|     {
 | |
|         m_error = kind;
 | |
|         m_error_description = move(description);
 | |
|         if (m_source_position.has_value() && position.has_value())
 | |
|             m_source_position.value().position = position.release_value();
 | |
|     }
 | |
|     bool has_error(ShellError err) const { return m_error == err; }
 | |
|     bool has_any_error() const { return !has_error(ShellError::None); }
 | |
|     ByteString const& error_description() const { return m_error_description; }
 | |
|     ShellError take_error()
 | |
|     {
 | |
|         auto err = m_error;
 | |
|         m_error = ShellError::None;
 | |
|         m_error_description = {};
 | |
|         return err;
 | |
|     }
 | |
|     void possibly_print_error() const;
 | |
|     static bool is_control_flow(ShellError error)
 | |
|     {
 | |
|         switch (error) {
 | |
|         case ShellError::InternalControlFlowBreak:
 | |
|         case ShellError::InternalControlFlowContinue:
 | |
|         case ShellError::InternalControlFlowReturn:
 | |
|         case ShellError::InternalControlFlowInterrupted:
 | |
|         case ShellError::InternalControlFlowKilled:
 | |
|             return true;
 | |
|         default:
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| #define __ENUMERATE_SHELL_OPTION(name, default_, description) \
 | |
|     bool name { default_ };
 | |
| 
 | |
|     struct Options {
 | |
|         ENUMERATE_SHELL_OPTIONS();
 | |
|     } options;
 | |
| 
 | |
| #undef __ENUMERATE_SHELL_OPTION
 | |
| 
 | |
| private:
 | |
|     Shell(Line::Editor&, bool attempt_interactive, bool posix_mode = false);
 | |
|     Shell();
 | |
|     virtual ~Shell() override;
 | |
| 
 | |
|     void destroy();
 | |
|     void initialize(bool attempt_interactive);
 | |
| 
 | |
|     RefPtr<AST::Node> parse(StringView, bool interactive = false, bool as_command = true) const;
 | |
| 
 | |
|     void timer_event(Core::TimerEvent&) override;
 | |
| 
 | |
|     void set_user_prompt();
 | |
| 
 | |
|     bool is_allowed_to_modify_termios(const AST::Command&) const;
 | |
| 
 | |
|     void bring_cursor_to_beginning_of_a_line() const;
 | |
| 
 | |
|     Optional<int> resolve_job_spec(StringView);
 | |
|     void add_entry_to_cache(RunnablePath const&);
 | |
|     void remove_entry_from_cache(StringView);
 | |
|     void stop_all_jobs();
 | |
|     Job* m_current_job { nullptr };
 | |
|     LocalFrame* find_frame_containing_local_variable(StringView name);
 | |
|     LocalFrame const* find_frame_containing_local_variable(StringView name) const
 | |
|     {
 | |
|         return const_cast<Shell*>(this)->find_frame_containing_local_variable(name);
 | |
|     }
 | |
| 
 | |
|     void run_tail(RefPtr<Job>);
 | |
|     void run_tail(const AST::Command&, const AST::NodeWithAction&, int head_exit_code);
 | |
| 
 | |
|     [[noreturn]] void execute_process(Vector<char const*>&& argv);
 | |
|     ErrorOr<void> execute_process(Span<StringView> argv);
 | |
| 
 | |
|     virtual void custom_event(Core::CustomEvent&) override;
 | |
| 
 | |
| #define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(name) \
 | |
|     ErrorOr<RefPtr<AST::Node>> immediate_##name(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const&);
 | |
| 
 | |
|     ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS();
 | |
| 
 | |
| #undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION
 | |
| 
 | |
|     ErrorOr<RefPtr<AST::Node>> immediate_length_impl(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const&, bool across);
 | |
| 
 | |
| #define __ENUMERATE_SHELL_BUILTIN(builtin, _mode) \
 | |
|     ErrorOr<int> builtin_##builtin(Main::Arguments);
 | |
| 
 | |
|     ENUMERATE_SHELL_BUILTINS();
 | |
| 
 | |
| #undef __ENUMERATE_SHELL_BUILTIN
 | |
| 
 | |
|     static constexpr Array builtin_names = {
 | |
| #define __ENUMERATE_SHELL_BUILTIN(builtin, _mode) #builtin##sv,
 | |
| 
 | |
|         ENUMERATE_SHELL_BUILTINS()
 | |
| 
 | |
| #undef __ENUMERATE_SHELL_BUILTIN
 | |
| 
 | |
|             "."sv, // Needs to be aliased to "source" in POSIX mode.
 | |
|         // clang-format off
 | |
|         // Clang-format does not properly indent this, it gives it 4 spaces too few.
 | |
|             ":"sv, // POSIX-y name for "noop".
 | |
|         // clang-format on
 | |
|     };
 | |
| 
 | |
|     struct ShellFunction {
 | |
|         ByteString name;
 | |
|         Vector<ByteString> arguments;
 | |
|         RefPtr<AST::Node> body;
 | |
|     };
 | |
| 
 | |
|     ErrorOr<String> serialize_function_definition(ShellFunction const&) const;
 | |
| 
 | |
|     bool m_should_ignore_jobs_on_next_exit { false };
 | |
|     pid_t m_pid { 0 };
 | |
| 
 | |
|     HashMap<ByteString, ShellFunction> m_functions;
 | |
|     Vector<NonnullOwnPtr<LocalFrame>> m_local_frames;
 | |
|     Promise::List m_active_promises;
 | |
|     Vector<NonnullRefPtr<AST::Redirection>> m_global_redirections;
 | |
| 
 | |
|     HashMap<ByteString, ByteString> m_aliases;
 | |
|     bool m_is_interactive { true };
 | |
|     bool m_is_subshell { false };
 | |
|     bool m_should_reinstall_signal_handlers { true };
 | |
|     bool m_in_posix_mode { false };
 | |
| 
 | |
|     ShellError m_error { ShellError::None };
 | |
|     ByteString m_error_description;
 | |
|     Optional<SourcePosition> m_source_position;
 | |
| 
 | |
|     bool m_should_format_live { false };
 | |
| 
 | |
|     RefPtr<Line::Editor> m_editor;
 | |
| 
 | |
|     bool m_default_constructed { false };
 | |
| 
 | |
|     mutable bool m_last_continuation_state { false }; // false == not needed.
 | |
| 
 | |
|     Optional<size_t> m_history_autosave_time;
 | |
| 
 | |
|     StackInfo m_completion_stack_info;
 | |
| 
 | |
|     RefPtr<AST::Node> m_prompt_command_node;
 | |
|     mutable Optional<ByteString> m_next_scheduled_prompt_text;
 | |
| };
 | |
| 
 | |
| [[maybe_unused]] static constexpr bool is_word_character(char c)
 | |
| {
 | |
|     return c == '_' || (c <= 'Z' && c >= 'A') || (c <= 'z' && c >= 'a') || (c <= '9' && c >= '0');
 | |
| }
 | |
| 
 | |
| inline size_t find_offset_into_node(StringView unescaped_text, size_t escaped_offset, Shell::EscapeMode escape_mode)
 | |
| {
 | |
|     size_t unescaped_offset = 0;
 | |
|     size_t offset = 0;
 | |
|     auto do_find_offset = [&](auto& unescaped_text) {
 | |
|         for (auto c : unescaped_text) {
 | |
|             if (offset == escaped_offset)
 | |
|                 return unescaped_offset;
 | |
| 
 | |
|             switch (Shell::special_character_escape_mode(c, escape_mode)) {
 | |
|             case Shell::SpecialCharacterEscapeMode::Untouched:
 | |
|                 break;
 | |
|             case Shell::SpecialCharacterEscapeMode::Escaped:
 | |
|                 ++offset; // X -> \X
 | |
|                 break;
 | |
|             case Shell::SpecialCharacterEscapeMode::QuotedAsEscape:
 | |
|                 switch (escape_mode) {
 | |
|                 case Shell::EscapeMode::Bareword:
 | |
|                     offset += 3; // X -> "\Y"
 | |
|                     break;
 | |
|                 case Shell::EscapeMode::SingleQuotedString:
 | |
|                     offset += 5; // X -> '"\Y"'
 | |
|                     break;
 | |
|                 case Shell::EscapeMode::DoubleQuotedString:
 | |
|                     offset += 1; // X -> \Y
 | |
|                     break;
 | |
|                 }
 | |
|                 break;
 | |
|             case Shell::SpecialCharacterEscapeMode::QuotedAsHex:
 | |
|                 switch (escape_mode) {
 | |
|                 case Shell::EscapeMode::Bareword:
 | |
|                     offset += 2; // X -> "\..."
 | |
|                     break;
 | |
|                 case Shell::EscapeMode::SingleQuotedString:
 | |
|                     offset += 4; // X -> '"\..."'
 | |
|                     break;
 | |
|                 case Shell::EscapeMode::DoubleQuotedString:
 | |
|                     // X -> \...
 | |
|                     break;
 | |
|                 }
 | |
|                 if (c > NumericLimits<u8>::max())
 | |
|                     offset += 8; // X -> "\uhhhhhhhh"
 | |
|                 else
 | |
|                     offset += 3; // X -> "\xhh"
 | |
|                 break;
 | |
|             }
 | |
|             ++offset;
 | |
|             ++unescaped_offset;
 | |
|         }
 | |
|         return unescaped_offset;
 | |
|     };
 | |
| 
 | |
|     Utf8View view { unescaped_text };
 | |
|     if (view.validate())
 | |
|         return do_find_offset(view);
 | |
|     return do_find_offset(unescaped_text);
 | |
| }
 | |
| 
 | |
| }
 | |
| 
 | |
| namespace AK {
 | |
| 
 | |
| template<>
 | |
| struct Traits<Shell::Shell::RunnablePath> : public DefaultTraits<Shell::Shell::RunnablePath> {
 | |
|     static constexpr bool is_trivial() { return false; }
 | |
| 
 | |
|     static bool equals(Shell::Shell::RunnablePath const& self, Shell::Shell::RunnablePath const& other)
 | |
|     {
 | |
|         return self == other;
 | |
|     }
 | |
| 
 | |
|     static bool equals(Shell::Shell::RunnablePath const& self, StringView other)
 | |
|     {
 | |
|         return self.path == other;
 | |
|     }
 | |
| 
 | |
|     static bool equals(Shell::Shell::RunnablePath const& self, ByteString const& other)
 | |
|     {
 | |
|         return self.path == other;
 | |
|     }
 | |
| };
 | |
| 
 | |
| }
 |