Coverage Report

Created: 2026-07-25 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibShell/Shell.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2020-2022, the SerenityOS developers.
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include "Job.h"
10
#include "Parser.h"
11
#include <AK/Array.h>
12
#include <AK/ByteString.h>
13
#include <AK/CircularQueue.h>
14
#include <AK/HashMap.h>
15
#include <AK/IntrusiveList.h>
16
#include <AK/StackInfo.h>
17
#include <AK/StringBuilder.h>
18
#include <AK/StringView.h>
19
#include <AK/Types.h>
20
#include <AK/Vector.h>
21
#include <LibCore/EventReceiver.h>
22
#include <LibCore/Notifier.h>
23
#include <LibLine/Editor.h>
24
#include <LibMain/Main.h>
25
#include <termios.h>
26
27
#define ENUMERATE_SHELL_BUILTINS()                           \
28
0
    __ENUMERATE_SHELL_BUILTIN(alias, InAllModes)             \
29
0
    __ENUMERATE_SHELL_BUILTIN(where, InAllModes)             \
30
0
    __ENUMERATE_SHELL_BUILTIN(cd, InAllModes)                \
31
0
    __ENUMERATE_SHELL_BUILTIN(cdh, InAllModes)               \
32
0
    __ENUMERATE_SHELL_BUILTIN(command, InAllModes)           \
33
0
    __ENUMERATE_SHELL_BUILTIN(pwd, InAllModes)               \
34
0
    __ENUMERATE_SHELL_BUILTIN(type, InAllModes)              \
35
0
    __ENUMERATE_SHELL_BUILTIN(exec, InAllModes)              \
36
0
    __ENUMERATE_SHELL_BUILTIN(eval, OnlyInPOSIXMode)         \
37
0
    __ENUMERATE_SHELL_BUILTIN(exit, InAllModes)              \
38
0
    __ENUMERATE_SHELL_BUILTIN(export, InAllModes)            \
39
0
    __ENUMERATE_SHELL_BUILTIN(glob, InAllModes)              \
40
0
    __ENUMERATE_SHELL_BUILTIN(unalias, InAllModes)           \
41
0
    __ENUMERATE_SHELL_BUILTIN(unset, InAllModes)             \
42
0
    __ENUMERATE_SHELL_BUILTIN(set, InAllModes)               \
43
0
    __ENUMERATE_SHELL_BUILTIN(history, InAllModes)           \
44
0
    __ENUMERATE_SHELL_BUILTIN(umask, InAllModes)             \
45
0
    __ENUMERATE_SHELL_BUILTIN(not, InAllModes)               \
46
0
    __ENUMERATE_SHELL_BUILTIN(dirs, InAllModes)              \
47
0
    __ENUMERATE_SHELL_BUILTIN(pushd, InAllModes)             \
48
0
    __ENUMERATE_SHELL_BUILTIN(popd, InAllModes)              \
49
0
    __ENUMERATE_SHELL_BUILTIN(setopt, InAllModes)            \
50
0
    __ENUMERATE_SHELL_BUILTIN(shift, InAllModes)             \
51
0
    __ENUMERATE_SHELL_BUILTIN(source, InAllModes)            \
52
0
    __ENUMERATE_SHELL_BUILTIN(time, InAllModes)              \
53
0
    __ENUMERATE_SHELL_BUILTIN(jobs, InAllModes)              \
54
0
    __ENUMERATE_SHELL_BUILTIN(disown, InAllModes)            \
55
0
    __ENUMERATE_SHELL_BUILTIN(fg, InAllModes)                \
56
0
    __ENUMERATE_SHELL_BUILTIN(bg, InAllModes)                \
57
0
    __ENUMERATE_SHELL_BUILTIN(wait, InAllModes)              \
58
0
    __ENUMERATE_SHELL_BUILTIN(dump, InAllModes)              \
59
0
    __ENUMERATE_SHELL_BUILTIN(kill, InAllModes)              \
60
0
    __ENUMERATE_SHELL_BUILTIN(reset, InAllModes)             \
61
0
    __ENUMERATE_SHELL_BUILTIN(noop, InAllModes)              \
62
0
    __ENUMERATE_SHELL_BUILTIN(break, OnlyInPOSIXMode)        \
63
0
    __ENUMERATE_SHELL_BUILTIN(continue, OnlyInPOSIXMode)     \
64
0
    __ENUMERATE_SHELL_BUILTIN(return, InAllModes)            \
65
0
    __ENUMERATE_SHELL_BUILTIN(read, OnlyInPOSIXMode)         \
66
0
    __ENUMERATE_SHELL_BUILTIN(run_with_env, OnlyInPOSIXMode) \
67
0
    __ENUMERATE_SHELL_BUILTIN(argsparser_parse, InAllModes)  \
68
0
    __ENUMERATE_SHELL_BUILTIN(in_parallel, InAllModes)       \
69
0
    __ENUMERATE_SHELL_BUILTIN(shell_set_active_prompt, InAllModes)
70
71
#define ENUMERATE_SHELL_OPTIONS()                                                                                    \
72
0
    __ENUMERATE_SHELL_OPTION(inline_exec_keep_empty_segments, false, "Keep empty segments in inline execute $(...)") \
73
0
    __ENUMERATE_SHELL_OPTION(verbose, false, "Announce every command that is about to be executed")                  \
74
0
    __ENUMERATE_SHELL_OPTION(invoke_program_for_autocomplete, false, "Attempt to use the program being completed itself for autocompletion via --complete")
75
76
#define ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS()                          \
77
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(concat_lists)                 \
78
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(length)                       \
79
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(length_across)                \
80
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(remove_suffix)                \
81
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(remove_prefix)                \
82
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(regex_replace)                \
83
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(filter_glob)                  \
84
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(split)                        \
85
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(join)                         \
86
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(value_or_default)             \
87
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(assign_default)               \
88
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(error_if_empty)               \
89
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(null_or_alternative)          \
90
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(defined_value_or_default)     \
91
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(assign_defined_default)       \
92
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(error_if_unset)               \
93
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(null_if_unset_or_alternative) \
94
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(length_of_variable)           \
95
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(reexpand)                     \
96
0
    __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(math)
97
98
namespace Shell {
99
100
class Shell;
101
102
enum class POSIXModeRequirement {
103
    OnlyInPOSIXMode,
104
    InAllModes,
105
};
106
107
class Shell : public Core::EventReceiver {
108
    C_OBJECT(Shell);
109
110
public:
111
    constexpr static auto local_init_file_path = "~/.shellrc";
112
    constexpr static auto global_init_file_path = "/etc/shellrc";
113
    constexpr static auto local_posix_init_file_path = "~/.posixshrc";
114
    constexpr static auto global_posix_init_file_path = "/etc/posixshrc";
115
116
0
    bool should_format_live() const { return m_should_format_live; }
117
0
    void set_live_formatting(bool value) { m_should_format_live = value; }
118
119
    void setup_signals();
120
    void setup_keybinds();
121
122
    struct SourcePosition {
123
        Optional<ByteString> source_file;
124
        ByteString literal_source_text;
125
        Optional<AST::Position> position;
126
    };
127
128
    struct RunnablePath {
129
        enum class Kind {
130
            Builtin,
131
            Function,
132
            Alias,
133
            Executable,
134
        };
135
136
        Kind kind;
137
        ByteString path;
138
139
        bool operator<(RunnablePath const& other) const
140
0
        {
141
0
            return path < other.path;
142
0
        }
143
144
0
        bool operator==(RunnablePath const&) const = default;
145
    };
146
147
    struct RunnablePathComparator {
148
        int operator()(RunnablePath const& lhs, RunnablePath const& rhs)
149
0
        {
150
0
            if (lhs.path > rhs.path)
151
0
                return 1;
152
153
0
            if (lhs.path < rhs.path)
154
0
                return -1;
155
156
0
            return 0;
157
0
        }
158
159
        int operator()(StringView lhs, RunnablePath const& rhs)
160
0
        {
161
0
            if (lhs > rhs.path)
162
0
                return 1;
163
164
0
            if (lhs < rhs.path)
165
0
                return -1;
166
167
0
            return 0;
168
0
        }
169
    };
170
171
    int run_command(StringView, Optional<SourcePosition> = {});
172
    Optional<RunnablePath> runnable_path_for(StringView);
173
    Optional<ByteString> help_path_for(Vector<RunnablePath> visited, RunnablePath const& runnable_path);
174
    ErrorOr<RefPtr<Job>> run_command(const AST::Command&);
175
    Vector<NonnullRefPtr<Job>> run_commands(Vector<AST::Command>&);
176
    bool run_file(ByteString const&, bool explicitly_invoked = true);
177
    ErrorOr<bool> run_builtin(const AST::Command&, Vector<NonnullRefPtr<AST::Rewiring>> const&, int& retval);
178
    bool has_builtin(StringView) const;
179
    ErrorOr<RefPtr<AST::Node>> run_immediate_function(StringView name, AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const&);
180
    static bool has_immediate_function(StringView);
181
    void block_on_job(RefPtr<Job>);
182
    void block_on_pipeline(RefPtr<AST::Pipeline>);
183
    ByteString prompt() const;
184
185
    static ByteString expand_tilde(StringView expression);
186
    static ErrorOr<Vector<ByteString>> expand_globs(StringView path, StringView base);
187
    static Vector<ByteString> expand_globs(Vector<StringView> path_segments, StringView base);
188
    ErrorOr<Vector<AST::Command>> expand_aliases(Vector<AST::Command>);
189
    ByteString resolve_path(ByteString) const;
190
    Optional<ByteString> resolve_alias(StringView) const;
191
192
    static bool has_history_event(StringView);
193
194
    ErrorOr<RefPtr<AST::Value const>> get_argument(size_t) const;
195
    ErrorOr<RefPtr<AST::Value const>> look_up_local_variable(StringView) const;
196
    ErrorOr<ByteString> local_variable_or(StringView, ByteString const&) const;
197
    void set_local_variable(ByteString const&, RefPtr<AST::Value>, bool only_in_current_frame = false);
198
    void unset_local_variable(StringView, bool only_in_current_frame = false);
199
200
    void define_function(ByteString name, Vector<ByteString> argnames, RefPtr<AST::Node> body);
201
    bool has_function(StringView);
202
    bool invoke_function(const AST::Command&, int& retval);
203
204
    ByteString format(StringView, ssize_t& cursor) const;
205
206
0
    RefPtr<Line::Editor> editor() const { return m_editor; }
207
208
    enum class LocalFrameKind {
209
        FunctionOrGlobal,
210
        Block,
211
    };
212
    struct LocalFrame {
213
        LocalFrame(ByteString name, HashMap<ByteString, RefPtr<AST::Value>> variables, LocalFrameKind kind = LocalFrameKind::Block)
214
0
            : name(move(name))
215
0
            , local_variables(move(variables))
216
0
            , is_function_frame(kind == LocalFrameKind::FunctionOrGlobal)
217
0
        {
218
0
        }
219
220
        ByteString name;
221
        HashMap<ByteString, RefPtr<AST::Value>> local_variables;
222
        bool is_function_frame;
223
    };
224
225
    struct Frame {
226
        Frame(Vector<NonnullOwnPtr<LocalFrame>>& frames, LocalFrame const& frame)
227
0
            : frames(frames)
228
0
            , frame(frame)
229
0
        {
230
0
        }
231
        ~Frame();
232
233
0
        void leak_frame() { should_destroy_frame = false; }
234
235
    private:
236
        Vector<NonnullOwnPtr<LocalFrame>>& frames;
237
        LocalFrame const& frame;
238
        bool should_destroy_frame { true };
239
    };
240
241
    [[nodiscard]] Frame push_frame(ByteString name, LocalFrameKind = LocalFrameKind::Block);
242
    void pop_frame();
243
244
    struct Promise {
245
        struct Data {
246
            struct Unveil {
247
                ByteString path;
248
                ByteString access;
249
            };
250
            ByteString exec_promises;
251
            Vector<Unveil> unveils;
252
        } data;
253
254
        IntrusiveListNode<Promise> node;
255
        using List = IntrusiveList<&Promise::node>;
256
    };
257
258
    struct ScopedPromise {
259
        ScopedPromise(Promise::List& promises, Promise&& promise)
260
0
            : promises(promises)
261
0
            , promise(move(promise))
262
0
        {
263
0
            promises.append(this->promise);
264
0
        }
265
266
        ~ScopedPromise()
267
0
        {
268
0
            promises.remove(promise);
269
0
        }
270
271
        Promise::List& promises;
272
        Promise promise;
273
    };
274
    [[nodiscard]] ScopedPromise promise(Promise::Data data)
275
0
    {
276
0
        return { m_active_promises, { move(data), {} } };
277
0
    }
278
279
    enum class EscapeMode {
280
        Bareword,
281
        SingleQuotedString,
282
        DoubleQuotedString,
283
    };
284
    static ByteString escape_token_for_double_quotes(StringView token);
285
    static ByteString escape_token_for_single_quotes(StringView token);
286
    static ByteString escape_token(StringView token, EscapeMode = EscapeMode::Bareword);
287
    static ByteString escape_token(Utf32View token, EscapeMode = EscapeMode::Bareword);
288
    static ByteString unescape_token(StringView token);
289
    enum class SpecialCharacterEscapeMode {
290
        Untouched,
291
        Escaped,
292
        QuotedAsEscape,
293
        QuotedAsHex,
294
    };
295
    static SpecialCharacterEscapeMode special_character_escape_mode(u32 c, EscapeMode);
296
297
    static bool is_glob(StringView);
298
299
    enum class ExecutableOnly {
300
        Yes,
301
        No
302
    };
303
304
    ErrorOr<void> highlight(Line::Editor&) const;
305
    Vector<Line::CompletionSuggestion> complete();
306
    Vector<Line::CompletionSuggestion> complete(StringView);
307
    Vector<Line::CompletionSuggestion> complete_program_name(StringView, size_t offset, EscapeMode = EscapeMode::Bareword);
308
    Vector<Line::CompletionSuggestion> complete_variable(StringView, size_t offset);
309
    Vector<Line::CompletionSuggestion> complete_user(StringView, size_t offset);
310
    Vector<Line::CompletionSuggestion> complete_immediate_function_name(StringView, size_t offset);
311
312
    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);
313
    Vector<Line::CompletionSuggestion> complete_option(StringView, StringView, size_t offset, AST::Node const* command_node, AST::Node const*);
314
    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);
315
316
    void restore_ios();
317
318
    u64 find_last_job_id() const;
319
    Job* find_job(u64 id, bool is_pid = false);
320
0
    Job* current_job() const { return m_current_job; }
321
    void kill_job(Job const*, int sig);
322
323
    ByteString get_history_path();
324
    void print_path(StringView path);
325
    void cache_path();
326
327
    bool read_single_line();
328
329
    void notify_child_event();
330
331
0
    bool posix_mode() const { return m_in_posix_mode; }
332
333
    Optional<struct termios> termios;
334
    Optional<struct termios> default_termios;
335
    bool was_interrupted { false };
336
    bool was_resized { false };
337
338
    ByteString cwd;
339
    ByteString username;
340
    ByteString home;
341
342
    constexpr static auto TTYNameSize = 32;
343
    constexpr static auto HostNameSize = 64;
344
345
    char ttyname[TTYNameSize];
346
    char hostname[HostNameSize];
347
348
    uid_t uid;
349
    Optional<int> last_return_code;
350
    Vector<ByteString> directory_stack;
351
    CircularQueue<ByteString, 8> cd_history; // FIXME: have a configurable cd history length
352
    HashMap<u64, NonnullRefPtr<Job>> jobs;
353
    Vector<RunnablePath, 256> cached_path;
354
355
    ByteString current_script;
356
357
    enum ShellEventType {
358
        ReadLine,
359
    };
360
361
    enum class ShellError {
362
        None,
363
        InternalControlFlowBreak,
364
        InternalControlFlowContinue,
365
        InternalControlFlowReturn,
366
        InternalControlFlowInterrupted,
367
        InternalControlFlowKilled,
368
        EvaluatedSyntaxError,
369
        NonExhaustiveMatchRules,
370
        InvalidGlobError,
371
        InvalidSliceContentsError,
372
        OpenFailure,
373
        OutOfMemory,
374
        LaunchError,
375
        PipeFailure,
376
        WriteFailure,
377
    };
378
379
    void raise_error(ShellError kind, ByteString description, Optional<AST::Position> position = {})
380
0
    {
381
0
        m_error = kind;
382
0
        m_error_description = move(description);
383
0
        if (m_source_position.has_value() && position.has_value())
384
0
            m_source_position.value().position = position.release_value();
385
0
    }
386
0
    bool has_error(ShellError err) const { return m_error == err; }
387
0
    bool has_any_error() const { return !has_error(ShellError::None); }
388
0
    ByteString const& error_description() const { return m_error_description; }
389
    ShellError take_error()
390
0
    {
391
0
        auto err = m_error;
392
0
        m_error = ShellError::None;
393
0
        m_error_description = {};
394
0
        return err;
395
0
    }
396
    void possibly_print_error() const;
397
    static bool is_control_flow(ShellError error)
398
0
    {
399
0
        switch (error) {
400
0
        case ShellError::InternalControlFlowBreak:
401
0
        case ShellError::InternalControlFlowContinue:
402
0
        case ShellError::InternalControlFlowReturn:
403
0
        case ShellError::InternalControlFlowInterrupted:
404
0
        case ShellError::InternalControlFlowKilled:
405
0
            return true;
406
0
        default:
407
0
            return false;
408
0
        }
409
0
    }
410
411
#define __ENUMERATE_SHELL_OPTION(name, default_, description) \
412
    bool name { default_ };
413
414
    struct Options {
415
        ENUMERATE_SHELL_OPTIONS();
416
    } options;
417
418
#undef __ENUMERATE_SHELL_OPTION
419
420
private:
421
    Shell(Line::Editor&, bool attempt_interactive, bool posix_mode = false);
422
    Shell();
423
    virtual ~Shell() override;
424
425
    void destroy();
426
    void initialize(bool attempt_interactive);
427
428
    RefPtr<AST::Node> parse(StringView, bool interactive = false, bool as_command = true) const;
429
430
    void timer_event(Core::TimerEvent&) override;
431
432
    void set_user_prompt();
433
434
    bool is_allowed_to_modify_termios(const AST::Command&) const;
435
436
    void bring_cursor_to_beginning_of_a_line() const;
437
438
    Optional<int> resolve_job_spec(StringView);
439
    void add_entry_to_cache(RunnablePath const&);
440
    void remove_entry_from_cache(StringView);
441
    void stop_all_jobs();
442
    Job* m_current_job { nullptr };
443
    LocalFrame* find_frame_containing_local_variable(StringView name);
444
    LocalFrame const* find_frame_containing_local_variable(StringView name) const
445
0
    {
446
0
        return const_cast<Shell*>(this)->find_frame_containing_local_variable(name);
447
0
    }
448
449
    void run_tail(RefPtr<Job>);
450
    void run_tail(const AST::Command&, const AST::NodeWithAction&, int head_exit_code);
451
452
    [[noreturn]] void execute_process(Vector<char const*>&& argv);
453
    ErrorOr<void> execute_process(Span<StringView> argv);
454
455
    virtual void custom_event(Core::CustomEvent&) override;
456
457
#define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(name) \
458
    ErrorOr<RefPtr<AST::Node>> immediate_##name(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const&);
459
460
    ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS();
461
462
#undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION
463
464
    ErrorOr<RefPtr<AST::Node>> immediate_length_impl(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const&, bool across);
465
466
#define __ENUMERATE_SHELL_BUILTIN(builtin, _mode) \
467
    ErrorOr<int> builtin_##builtin(Main::Arguments);
468
469
    ENUMERATE_SHELL_BUILTINS();
470
471
#undef __ENUMERATE_SHELL_BUILTIN
472
473
    static constexpr Array builtin_names = {
474
#define __ENUMERATE_SHELL_BUILTIN(builtin, _mode) #builtin##sv,
475
476
        ENUMERATE_SHELL_BUILTINS()
477
478
#undef __ENUMERATE_SHELL_BUILTIN
479
480
            "."sv, // Needs to be aliased to "source" in POSIX mode.
481
        // clang-format off
482
        // Clang-format does not properly indent this, it gives it 4 spaces too few.
483
            ":"sv, // POSIX-y name for "noop".
484
        // clang-format on
485
    };
486
487
    struct ShellFunction {
488
        ByteString name;
489
        Vector<ByteString> arguments;
490
        RefPtr<AST::Node> body;
491
    };
492
493
    ErrorOr<String> serialize_function_definition(ShellFunction const&) const;
494
495
    bool m_should_ignore_jobs_on_next_exit { false };
496
    pid_t m_pid { 0 };
497
498
    HashMap<ByteString, ShellFunction> m_functions;
499
    Vector<NonnullOwnPtr<LocalFrame>> m_local_frames;
500
    Promise::List m_active_promises;
501
    Vector<NonnullRefPtr<AST::Redirection>> m_global_redirections;
502
503
    HashMap<ByteString, ByteString> m_aliases;
504
    bool m_is_interactive { true };
505
    bool m_is_subshell { false };
506
    bool m_should_reinstall_signal_handlers { true };
507
    bool m_in_posix_mode { false };
508
509
    ShellError m_error { ShellError::None };
510
    ByteString m_error_description;
511
    Optional<SourcePosition> m_source_position;
512
513
    bool m_should_format_live { false };
514
515
    RefPtr<Line::Editor> m_editor;
516
517
    bool m_default_constructed { false };
518
519
    mutable bool m_last_continuation_state { false }; // false == not needed.
520
521
    Optional<size_t> m_history_autosave_time;
522
523
    StackInfo m_completion_stack_info;
524
525
    RefPtr<AST::Node> m_prompt_command_node;
526
    mutable Optional<ByteString> m_next_scheduled_prompt_text;
527
};
528
529
[[maybe_unused]] static constexpr bool is_word_character(char c)
530
23.9M
{
531
23.9M
    return c == '_' || (c <= 'Z' && c >= 'A') || (c <= 'z' && c >= 'a') || (c <= '9' && c >= '0');
532
23.9M
}
Unexecuted instantiation: FuzzShell.cpp:Shell::is_word_character(char)
Parser.cpp:Shell::is_word_character(char)
Line
Count
Source
530
23.9M
{
531
23.9M
    return c == '_' || (c <= 'Z' && c >= 'A') || (c <= 'z' && c >= 'a') || (c <= '9' && c >= '0');
532
23.9M
}
Unexecuted instantiation: AST.cpp:Shell::is_word_character(char)
Unexecuted instantiation: ImmediateFunctions.cpp:Shell::is_word_character(char)
Unexecuted instantiation: Shell.cpp:Shell::is_word_character(char)
Unexecuted instantiation: Builtin.cpp:Shell::is_word_character(char)
Unexecuted instantiation: Job.cpp:Shell::is_word_character(char)
Unexecuted instantiation: URLHighlight.cpp:Shell::is_word_character(char)
Unexecuted instantiation: FuzzShellPosix.cpp:Shell::is_word_character(char)
533
534
inline size_t find_offset_into_node(StringView unescaped_text, size_t escaped_offset, Shell::EscapeMode escape_mode)
535
0
{
536
0
    size_t unescaped_offset = 0;
537
0
    size_t offset = 0;
538
0
    auto do_find_offset = [&](auto& unescaped_text) {
539
0
        for (auto c : unescaped_text) {
540
0
            if (offset == escaped_offset)
541
0
                return unescaped_offset;
542
543
0
            switch (Shell::special_character_escape_mode(c, escape_mode)) {
544
0
            case Shell::SpecialCharacterEscapeMode::Untouched:
545
0
                break;
546
0
            case Shell::SpecialCharacterEscapeMode::Escaped:
547
0
                ++offset; // X -> \X
548
0
                break;
549
0
            case Shell::SpecialCharacterEscapeMode::QuotedAsEscape:
550
0
                switch (escape_mode) {
551
0
                case Shell::EscapeMode::Bareword:
552
0
                    offset += 3; // X -> "\Y"
553
0
                    break;
554
0
                case Shell::EscapeMode::SingleQuotedString:
555
0
                    offset += 5; // X -> '"\Y"'
556
0
                    break;
557
0
                case Shell::EscapeMode::DoubleQuotedString:
558
0
                    offset += 1; // X -> \Y
559
0
                    break;
560
0
                }
561
0
                break;
562
0
            case Shell::SpecialCharacterEscapeMode::QuotedAsHex:
563
0
                switch (escape_mode) {
564
0
                case Shell::EscapeMode::Bareword:
565
0
                    offset += 2; // X -> "\..."
566
0
                    break;
567
0
                case Shell::EscapeMode::SingleQuotedString:
568
0
                    offset += 4; // X -> '"\..."'
569
0
                    break;
570
0
                case Shell::EscapeMode::DoubleQuotedString:
571
                    // X -> \...
572
0
                    break;
573
0
                }
574
0
                if (c > NumericLimits<u8>::max())
575
0
                    offset += 8; // X -> "\uhhhhhhhh"
576
0
                else
577
0
                    offset += 3; // X -> "\xhh"
578
0
                break;
579
0
            }
580
0
            ++offset;
581
0
            ++unescaped_offset;
582
0
        }
583
0
        return unescaped_offset;
584
0
    };
Unexecuted instantiation: auto Shell::find_offset_into_node(AK::StringView, unsigned long, Shell::Shell::EscapeMode)::{lambda(auto:1&)#1}::operator()<AK::Utf8View>(AK::Utf8View&) const
Unexecuted instantiation: auto Shell::find_offset_into_node(AK::StringView, unsigned long, Shell::Shell::EscapeMode)::{lambda(auto:1&)#1}::operator()<AK::StringView>(AK::StringView&) const
585
586
0
    Utf8View view { unescaped_text };
587
0
    if (view.validate())
588
0
        return do_find_offset(view);
589
0
    return do_find_offset(unescaped_text);
590
0
}
591
592
}
593
594
namespace AK {
595
596
template<>
597
struct Traits<Shell::Shell::RunnablePath> : public DefaultTraits<Shell::Shell::RunnablePath> {
598
0
    static constexpr bool is_trivial() { return false; }
599
600
    static bool equals(Shell::Shell::RunnablePath const& self, Shell::Shell::RunnablePath const& other)
601
0
    {
602
0
        return self == other;
603
0
    }
604
605
    static bool equals(Shell::Shell::RunnablePath const& self, StringView other)
606
0
    {
607
0
        return self.path == other;
608
0
    }
609
610
    static bool equals(Shell::Shell::RunnablePath const& self, ByteString const& other)
611
0
    {
612
0
        return self.path == other;
613
0
    }
614
};
615
616
}