#pragma once
#include "tokenizer/base.hpp"
#include <cstddef>
#include <cstdint>
#include <ostream>
#include <string_view>
#include <unordered_set>
#include <vector>
namespace ddwaf {
enum class shell_token_type : uint8_t {
unknown,
whitespace,
executable,
field,
arithmetic_expansion,
arithmetic_expansion_open,
arithmetic_expansion_close,
double_quoted_string_open,
double_quoted_string_close,
double_quoted_string,
single_quoted_string,
control,
variable_definition,
variable,
equal,
backtick_substitution_open,
backtick_substitution_close,
dollar,
redirection,
command_substitution_open,
command_substitution_close,
parenthesis_open,
parenthesis_close,
curly_brace_open,
curly_brace_close,
process_substitution_open,
process_substitution_close,
subshell_open,
subshell_close,
compound_command_open,
compound_command_close,
array_open,
array_close,
parameter_expansion_open,
parameter_expansion_close,
file_redirection_open,
file_redirection_close,
};
using shell_token = base_token<shell_token_type>;
std::ostream &operator<<(std::ostream &os, shell_token_type type);
class shell_tokenizer : protected base_tokenizer<shell_token_type> {
public:
explicit shell_tokenizer(
std::string_view str, std::unordered_set<shell_token_type> skip_tokens = {});
std::vector<shell_token> tokenize();
static bool initialise_regexes();
protected:
enum class shell_scope : uint8_t {
global,
double_quoted_string, backtick_substitution, command_substitution, compound_command, subshell, process_substitution, legacy_arithmetic_expansion, arithmetic_expansion, array, file_redirection, parameter_expansion, };
std::vector<shell_scope> shell_scope_stack_;
shell_scope current_shell_scope_{shell_scope::global};
void push_shell_scope(shell_scope scope)
{
current_shell_scope_ = scope;
shell_scope_stack_.emplace_back(scope);
}
void pop_shell_scope()
{
shell_scope_stack_.pop_back();
current_shell_scope_ = shell_scope_stack_.back();
}
template <typename T, typename... Rest>
bool match_nth_nonws_token_descending(std::size_t n, T expected, Rest... args) const
{
const auto &nth_token = tokens_[n];
if (nth_token.type == shell_token_type::whitespace) {
return n > 0 && match_nth_nonws_token_descending(n - 1, expected, args...);
}
bool res = false;
if constexpr (std::is_same_v<T, shell_token_type>) {
res = (nth_token.type == expected);
}
if constexpr (std::is_same_v<T, std::string_view>) {
res = (nth_token.str == expected);
}
if constexpr (sizeof...(args) > 0) {
return n > 0 && res && match_nth_nonws_token_descending(n - 1, args...);
} else {
return res;
}
}
template <typename... Args> bool match_last_n_nonws_tokens(Args... args) const
{
if (tokens_.size() < sizeof...(Args)) {
return false;
}
return match_nth_nonws_token_descending(tokens_.size() - 1, args...);
}
template <typename T, typename... Rest>
bool match_last_nonws_token_with_one_of_T(
const shell_token &obtained, T expected, Rest... args) const
{
bool res = false;
if constexpr (std::is_same_v<T, shell_token_type>) {
res = (obtained.type == expected);
}
if constexpr (std::is_same_v<T, std::string_view> ||
std::is_constructible_v<std::string_view, T>) {
res = (obtained.str == expected);
}
if constexpr (sizeof...(args) > 0) {
return res || match_last_nonws_token_with_one_of_T(obtained, args...);
} else {
return res;
}
}
template <typename... Args> bool match_last_nonws_token_with_one_of(Args... args) const
{
auto last_it = tokens_.rbegin();
if (last_it != tokens_.rend() && last_it->type == shell_token_type::whitespace) {
++last_it;
}
if (last_it == tokens_.rend()) {
return false;
}
return match_last_nonws_token_with_one_of_T(*last_it, args...);
}
bool is_beginning_of_command()
{
return tokens_.empty() || match_last_nonws_token_with_one_of(shell_token_type::control,
shell_token_type::backtick_substitution_open,
shell_token_type::command_substitution_open,
shell_token_type::process_substitution_open,
shell_token_type::compound_command_open,
shell_token_type::subshell_open, shell_token_type::control);
}
void tokenize_single_quoted_string();
void tokenize_expandable_scope(std::string_view delimiter, shell_token_type full_token,
shell_token_type open_token, shell_token_type close_token);
void tokenize_variable();
void tokenize_parameter_expansion();
void tokenize_field(shell_token_type type = shell_token_type::field);
void tokenize_redirection();
void tokenize_redirection_or_field();
void tokenize_delimited_token(std::string_view delimiter, shell_token_type type);
};
}