mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-12 12:01:14 -03:00
committed by
Johannes Altmanninger
parent
5d37698ef8
commit
17ba602acf
196
src/ast.rs
196
src/ast.rs
@@ -634,7 +634,7 @@ pub struct Redirection {
|
||||
|
||||
impl CheckParse for Redirection {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
pop.peek_type(0) == ParseTokenType::redirection
|
||||
pop.peek_type(0) == ParseTokenType::Redirection
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,7 +699,7 @@ pub fn redirection(&self) -> &Redirection {
|
||||
impl CheckParse for ArgumentOrRedirection {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
let typ = pop.peek_type(0);
|
||||
matches!(typ, ParseTokenType::string | ParseTokenType::redirection)
|
||||
matches!(typ, ParseTokenType::String | ParseTokenType::Redirection)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -793,8 +793,8 @@ impl CheckParse for JobConjunction {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
let token = pop.peek_token(0);
|
||||
// These keywords end a job list.
|
||||
token.typ == ParseTokenType::left_brace
|
||||
|| (token.typ == ParseTokenType::string
|
||||
token.typ == ParseTokenType::LeftBrace
|
||||
|| (token.typ == ParseTokenType::String
|
||||
&& !matches!(
|
||||
token.keyword,
|
||||
ParseKeyword::Case | ParseKeyword::End | ParseKeyword::Else
|
||||
@@ -976,7 +976,7 @@ pub struct JobContinuation {
|
||||
}
|
||||
impl CheckParse for JobContinuation {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
pop.peek_type(0) == ParseTokenType::pipe
|
||||
pop.peek_type(0) == ParseTokenType::Pipe
|
||||
}
|
||||
}
|
||||
|
||||
@@ -993,7 +993,7 @@ pub struct JobConjunctionContinuation {
|
||||
impl CheckParse for JobConjunctionContinuation {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
let typ = pop.peek_type(0);
|
||||
matches!(typ, ParseTokenType::andand | ParseTokenType::oror)
|
||||
matches!(typ, ParseTokenType::AndAnd | ParseTokenType::OrOr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1015,7 +1015,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
let next_token = pop.peek_token(1);
|
||||
matches!(
|
||||
next_token.typ,
|
||||
ParseTokenType::string | ParseTokenType::left_brace
|
||||
ParseTokenType::String | ParseTokenType::LeftBrace
|
||||
) && !next_token.is_help_argument
|
||||
}
|
||||
}
|
||||
@@ -1053,9 +1053,9 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
// What is the token after it?
|
||||
match pop.peek_type(1) {
|
||||
// We have `a= cmd` and should treat it as a variable assignment.
|
||||
ParseTokenType::string | ParseTokenType::left_brace => true,
|
||||
ParseTokenType::String | ParseTokenType::LeftBrace => true,
|
||||
// We have `a=` which is OK if we are allowing incomplete, an error otherwise.
|
||||
ParseTokenType::terminate => pop.allow_incomplete(),
|
||||
ParseTokenType::Terminate => pop.allow_incomplete(),
|
||||
// We have e.g. `a= >` which is an error.
|
||||
// Note that we do not produce an error here. Instead we return false
|
||||
// so this the token will be seen by allocate_populate_statement.
|
||||
@@ -1078,18 +1078,18 @@ pub struct Argument {
|
||||
}
|
||||
impl CheckParse for Argument {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
pop.peek_type(0) == ParseTokenType::string
|
||||
pop.peek_type(0) == ParseTokenType::String
|
||||
}
|
||||
}
|
||||
|
||||
define_token_node!(SemiNl, end);
|
||||
define_token_node!(String_, string);
|
||||
define_token_node!(TokenBackground, background);
|
||||
define_token_node!(TokenConjunction, andand, oror);
|
||||
define_token_node!(TokenPipe, pipe);
|
||||
define_token_node!(TokenLeftBrace, left_brace);
|
||||
define_token_node!(TokenRightBrace, right_brace);
|
||||
define_token_node!(TokenRedirection, redirection);
|
||||
define_token_node!(SemiNl, End);
|
||||
define_token_node!(String_, String);
|
||||
define_token_node!(TokenBackground, Background);
|
||||
define_token_node!(TokenConjunction, AndAnd, OrOr);
|
||||
define_token_node!(TokenPipe, Pipe);
|
||||
define_token_node!(TokenLeftBrace, LeftBrace);
|
||||
define_token_node!(TokenRightBrace, RightBrace);
|
||||
define_token_node!(TokenRedirection, Redirection);
|
||||
|
||||
define_keyword_node!(DecoratedStatementDecorator, Command, Builtin, Exec);
|
||||
define_keyword_node!(JobConjunctionDecorator, And, Or);
|
||||
@@ -1132,7 +1132,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
return false;
|
||||
}
|
||||
let next_token = pop.peek_token(1);
|
||||
next_token.typ == ParseTokenType::string && !next_token.is_dash_prefix_string()
|
||||
next_token.typ == ParseTokenType::String && !next_token.is_dash_prefix_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1151,13 +1151,13 @@ impl DecoratedStatement {
|
||||
/// Return the decoration for this statement.
|
||||
pub fn decoration(&self) -> StatementDecoration {
|
||||
let Some(decorator) = &self.opt_decoration else {
|
||||
return StatementDecoration::none;
|
||||
return StatementDecoration::None;
|
||||
};
|
||||
let decorator: &dyn Keyword = decorator;
|
||||
match decorator.keyword() {
|
||||
ParseKeyword::Command => StatementDecoration::command,
|
||||
ParseKeyword::Builtin => StatementDecoration::builtin,
|
||||
ParseKeyword::Exec => StatementDecoration::exec,
|
||||
ParseKeyword::Command => StatementDecoration::Command,
|
||||
ParseKeyword::Builtin => StatementDecoration::Builtin,
|
||||
ParseKeyword::Exec => StatementDecoration::Exec,
|
||||
_ => panic!("Unexpected keyword in statement decoration"),
|
||||
}
|
||||
}
|
||||
@@ -1452,22 +1452,22 @@ pub fn dump(&self, orig: &wstr) -> WString {
|
||||
sprintf!(=> &mut result, "keyword: %s", n.keyword().to_wstr());
|
||||
} else if let Some(n) = node.as_token() {
|
||||
let desc = match n.token_type() {
|
||||
ParseTokenType::string => {
|
||||
ParseTokenType::String => {
|
||||
let mut desc = WString::from_str("string");
|
||||
if let Some(strsource) = n.try_source(orig) {
|
||||
sprintf!(=> &mut desc, ": '%s'", strsource);
|
||||
}
|
||||
desc
|
||||
}
|
||||
ParseTokenType::redirection => {
|
||||
ParseTokenType::Redirection => {
|
||||
let mut desc = WString::from_str("redirection");
|
||||
if let Some(strsource) = n.try_source(orig) {
|
||||
sprintf!(=> &mut desc, ": '%s'", strsource);
|
||||
}
|
||||
desc
|
||||
}
|
||||
ParseTokenType::end => WString::from_str("<;>"),
|
||||
ParseTokenType::invalid => {
|
||||
ParseTokenType::End => WString::from_str("<;>"),
|
||||
ParseTokenType::Invalid => {
|
||||
// This may occur with errors, e.g. we expected to see a string but saw a
|
||||
// redirection.
|
||||
WString::from_str("<error>")
|
||||
@@ -1550,7 +1550,7 @@ fn new(src: &'a wstr, flags: ParseTreeFlags, freestanding_arguments: bool) -> Se
|
||||
flags |= TOK_ARGUMENT_LIST;
|
||||
}
|
||||
Self {
|
||||
lookahead: [ParseToken::new(ParseTokenType::invalid); Self::MAX_LOOKAHEAD],
|
||||
lookahead: [ParseToken::new(ParseTokenType::Invalid); Self::MAX_LOOKAHEAD],
|
||||
start: 0,
|
||||
count: 0,
|
||||
src,
|
||||
@@ -1593,7 +1593,7 @@ fn mask(idx: usize) -> usize {
|
||||
fn next_from_tok(&mut self) -> ParseToken {
|
||||
loop {
|
||||
let res = self.advance_1();
|
||||
if res.typ == ParseTokenType::comment {
|
||||
if res.typ == ParseTokenType::Comment {
|
||||
self.comment_ranges.push(res.range());
|
||||
continue;
|
||||
}
|
||||
@@ -1605,7 +1605,7 @@ fn next_from_tok(&mut self) -> ParseToken {
|
||||
/// This returns comments.
|
||||
fn advance_1(&mut self) -> ParseToken {
|
||||
let Some(token) = self.tok.next() else {
|
||||
return ParseToken::new(ParseTokenType::terminate);
|
||||
return ParseToken::new(ParseTokenType::Terminate);
|
||||
};
|
||||
// Set the type, keyword, and whether there's a dash prefix. Note that this is quite
|
||||
// sketchy, because it ignores quotes. This is the historical behavior. For example,
|
||||
@@ -1617,7 +1617,7 @@ fn advance_1(&mut self) -> ParseToken {
|
||||
result.keyword = keyword_for_token(token.type_, text);
|
||||
result.has_dash_prefix = text.starts_with('-');
|
||||
result.is_help_argument = [L!("-h"), L!("--help")].contains(&text);
|
||||
result.is_newline = result.typ == ParseTokenType::end && text == "\n";
|
||||
result.is_newline = result.typ == ParseTokenType::End && text == "\n";
|
||||
result.may_be_variable_assignment = variable_assignment_equals_pos(text).is_some();
|
||||
result.tok_error = token.error;
|
||||
|
||||
@@ -1625,7 +1625,7 @@ fn advance_1(&mut self) -> ParseToken {
|
||||
result.set_source_start(token.offset());
|
||||
result.set_source_length(token.length());
|
||||
|
||||
if token.error != TokenizerError::none {
|
||||
if token.error != TokenizerError::None {
|
||||
let subtoken_offset = token.error_offset_within_token();
|
||||
// Skip invalid tokens that have a zero length, especially if they are at EOF.
|
||||
if subtoken_offset < result.source_length() {
|
||||
@@ -1823,13 +1823,13 @@ fn did_visit_fields_of<'a, N: NodeMut>(&'a mut self, node: &'a mut N, flow: Visi
|
||||
|
||||
let token = &error.token;
|
||||
// To-do: maybe extend this to other tokenizer errors?
|
||||
if token.typ == ParseTokenType::tokenizer_error
|
||||
&& token.tok_error == TokenizerError::closing_unopened_brace
|
||||
if token.typ == ParseTokenType::TokenizerError
|
||||
&& token.tok_error == TokenizerError::ClosingUnopenedBrace
|
||||
{
|
||||
parse_error_range!(
|
||||
self,
|
||||
token.range(),
|
||||
ParseErrorCode::unbalancing_brace,
|
||||
ParseErrorCode::UnbalancingBrace,
|
||||
"%s",
|
||||
<TokenizerError as Into<&wstr>>::into(token.tok_error)
|
||||
);
|
||||
@@ -1868,7 +1868,7 @@ fn did_visit_fields_of<'a, N: NodeMut>(&'a mut self, node: &'a mut N, flow: Visi
|
||||
|
||||
if let Some((header_kw_range, enclosing_stmt)) = header {
|
||||
let next_token = self.peek_token(0);
|
||||
if next_token.typ == ParseTokenType::string
|
||||
if next_token.typ == ParseTokenType::String
|
||||
&& matches!(
|
||||
next_token.keyword,
|
||||
ParseKeyword::Case | ParseKeyword::Else | ParseKeyword::End
|
||||
@@ -1879,7 +1879,7 @@ fn did_visit_fields_of<'a, N: NodeMut>(&'a mut self, node: &'a mut N, flow: Visi
|
||||
parse_error_range!(
|
||||
self,
|
||||
header_kw_range,
|
||||
ParseErrorCode::generic,
|
||||
ParseErrorCode::Generic,
|
||||
"Missing end to balance this %s",
|
||||
enclosing_stmt
|
||||
);
|
||||
@@ -1887,7 +1887,7 @@ fn did_visit_fields_of<'a, N: NodeMut>(&'a mut self, node: &'a mut N, flow: Visi
|
||||
parse_error!(
|
||||
self,
|
||||
token,
|
||||
ParseErrorCode::generic,
|
||||
ParseErrorCode::Generic,
|
||||
"Expected %s, but found %s",
|
||||
keywords_user_presentable_description(error.allowed_keywords),
|
||||
error.token.user_presentable_description(),
|
||||
@@ -1965,7 +1965,7 @@ fn status(&mut self) -> ParserStatus {
|
||||
if self.unwinding {
|
||||
ParserStatus::unwinding
|
||||
} else if self.flags.contains(ParseTreeFlags::LEAVE_UNTERMINATED)
|
||||
&& self.peek_type(0) == ParseTokenType::terminate
|
||||
&& self.peek_type(0) == ParseTokenType::Terminate
|
||||
{
|
||||
ParserStatus::unsourcing
|
||||
} else {
|
||||
@@ -2094,10 +2094,10 @@ fn chomp_extras(&mut self, kind: Kind) {
|
||||
let chomp_newlines = self.list_kind_chomps_newlines(kind);
|
||||
loop {
|
||||
let peek = self.tokens.peek(0);
|
||||
if chomp_newlines && peek.typ == ParseTokenType::end && peek.is_newline {
|
||||
if chomp_newlines && peek.typ == ParseTokenType::End && peek.is_newline {
|
||||
// Just skip this newline, no need to save it.
|
||||
self.tokens.pop();
|
||||
} else if chomp_semis && peek.typ == ParseTokenType::end && !peek.is_newline {
|
||||
} else if chomp_semis && peek.typ == ParseTokenType::End && !peek.is_newline {
|
||||
let tok = self.tokens.pop();
|
||||
// Perhaps save this extra semi.
|
||||
if self.flags.contains(ParseTreeFlags::SHOW_EXTRA_SEMIS) {
|
||||
@@ -2132,11 +2132,11 @@ fn peek_type(&mut self, idx: usize) -> ParseTokenType {
|
||||
fn consume_any_token(&mut self) -> ParseToken {
|
||||
let tok = self.tokens.pop();
|
||||
assert!(
|
||||
tok.typ != ParseTokenType::comment,
|
||||
tok.typ != ParseTokenType::Comment,
|
||||
"Should not be a comment"
|
||||
);
|
||||
assert!(
|
||||
tok.typ != ParseTokenType::terminate,
|
||||
tok.typ != ParseTokenType::Terminate,
|
||||
"Cannot consume terminate token, caller should check status first"
|
||||
);
|
||||
tok
|
||||
@@ -2145,7 +2145,7 @@ fn consume_any_token(&mut self) -> ParseToken {
|
||||
/// Consume the next token which is expected to be of the given type.
|
||||
fn consume_token_type(&mut self, typ: ParseTokenType) -> SourceRange {
|
||||
assert!(
|
||||
typ != ParseTokenType::terminate,
|
||||
typ != ParseTokenType::Terminate,
|
||||
"Should not attempt to consume terminate token"
|
||||
);
|
||||
let tok = self.consume_any_token();
|
||||
@@ -2153,7 +2153,7 @@ fn consume_token_type(&mut self, typ: ParseTokenType) -> SourceRange {
|
||||
parse_error!(
|
||||
self,
|
||||
tok,
|
||||
ParseErrorCode::generic,
|
||||
ParseErrorCode::Generic,
|
||||
"Expected %s, but found %s",
|
||||
token_type_user_presentable_description(typ, ParseKeyword::None),
|
||||
tok.user_presentable_description()
|
||||
@@ -2178,23 +2178,23 @@ fn consume_excess_token_generating_error(&mut self) {
|
||||
parse_error!(
|
||||
self,
|
||||
tok,
|
||||
ParseErrorCode::generic,
|
||||
ParseErrorCode::Generic,
|
||||
"Expected %s, but found %s",
|
||||
token_type_user_presentable_description(ParseTokenType::string, ParseKeyword::None),
|
||||
token_type_user_presentable_description(ParseTokenType::String, ParseKeyword::None),
|
||||
tok.user_presentable_description()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
match tok.typ {
|
||||
ParseTokenType::string => {
|
||||
ParseTokenType::String => {
|
||||
// There are three keywords which end a job list.
|
||||
match tok.keyword {
|
||||
ParseKeyword::Case => {
|
||||
parse_error!(
|
||||
self,
|
||||
tok,
|
||||
ParseErrorCode::unbalancing_case,
|
||||
ParseErrorCode::UnbalancingCase,
|
||||
"'case' builtin not inside of switch block"
|
||||
);
|
||||
}
|
||||
@@ -2202,7 +2202,7 @@ fn consume_excess_token_generating_error(&mut self) {
|
||||
parse_error!(
|
||||
self,
|
||||
tok,
|
||||
ParseErrorCode::unbalancing_end,
|
||||
ParseErrorCode::UnbalancingEnd,
|
||||
"'end' outside of a block"
|
||||
);
|
||||
}
|
||||
@@ -2210,7 +2210,7 @@ fn consume_excess_token_generating_error(&mut self) {
|
||||
parse_error!(
|
||||
self,
|
||||
tok,
|
||||
ParseErrorCode::unbalancing_else,
|
||||
ParseErrorCode::UnbalancingElse,
|
||||
"'else' builtin not inside of if block"
|
||||
);
|
||||
}
|
||||
@@ -2224,30 +2224,30 @@ fn consume_excess_token_generating_error(&mut self) {
|
||||
}
|
||||
}
|
||||
}
|
||||
ParseTokenType::redirection if self.peek_type(0) == ParseTokenType::string => {
|
||||
ParseTokenType::Redirection if self.peek_type(0) == ParseTokenType::String => {
|
||||
let next = self.tokens.pop();
|
||||
parse_error_range!(
|
||||
self,
|
||||
next.range().combine(tok.range()),
|
||||
ParseErrorCode::generic,
|
||||
ParseErrorCode::Generic,
|
||||
"Expected a string, but found a redirection"
|
||||
);
|
||||
}
|
||||
ParseTokenType::pipe
|
||||
| ParseTokenType::redirection
|
||||
| ParseTokenType::right_brace
|
||||
| ParseTokenType::background
|
||||
| ParseTokenType::andand
|
||||
| ParseTokenType::oror => {
|
||||
ParseTokenType::Pipe
|
||||
| ParseTokenType::Redirection
|
||||
| ParseTokenType::RightBrace
|
||||
| ParseTokenType::Background
|
||||
| ParseTokenType::AndAnd
|
||||
| ParseTokenType::OrOr => {
|
||||
parse_error!(
|
||||
self,
|
||||
tok,
|
||||
ParseErrorCode::generic,
|
||||
ParseErrorCode::Generic,
|
||||
"Expected a string, but found %s",
|
||||
tok.user_presentable_description()
|
||||
);
|
||||
}
|
||||
ParseTokenType::tokenizer_error => {
|
||||
ParseTokenType::TokenizerError => {
|
||||
parse_error!(
|
||||
self,
|
||||
tok,
|
||||
@@ -2256,14 +2256,14 @@ fn consume_excess_token_generating_error(&mut self) {
|
||||
tok.tok_error
|
||||
);
|
||||
}
|
||||
ParseTokenType::end => {
|
||||
ParseTokenType::End => {
|
||||
internal_error!(
|
||||
self,
|
||||
consume_excess_token_generating_error,
|
||||
"End token should never be excess"
|
||||
);
|
||||
}
|
||||
ParseTokenType::terminate => {
|
||||
ParseTokenType::Terminate => {
|
||||
internal_error!(
|
||||
self,
|
||||
consume_excess_token_generating_error,
|
||||
@@ -2325,7 +2325,7 @@ fn populate_list<Contents, List>(&mut self, list: &mut List, exhaust_stream: boo
|
||||
let typ = self.peek_type(0);
|
||||
if matches!(
|
||||
typ,
|
||||
ParseTokenType::string | ParseTokenType::terminate | ParseTokenType::end
|
||||
ParseTokenType::String | ParseTokenType::Terminate | ParseTokenType::End
|
||||
) {
|
||||
break;
|
||||
}
|
||||
@@ -2356,7 +2356,7 @@ fn populate_list<Contents, List>(&mut self, list: &mut List, exhaust_stream: boo
|
||||
contents.reserve(16);
|
||||
}
|
||||
contents.push(node);
|
||||
} else if exhaust_stream && self.peek_type(0) != ParseTokenType::terminate {
|
||||
} else if exhaust_stream && self.peek_type(0) != ParseTokenType::Terminate {
|
||||
// We aren't allowed to stop. Produce an error and keep going.
|
||||
self.consume_excess_token_generating_error()
|
||||
} else {
|
||||
@@ -2397,14 +2397,14 @@ fn got_error(slf: &mut Populator<'_>) -> Statement {
|
||||
|
||||
fn new_decorated_statement(slf: &mut Populator<'_>) -> Statement {
|
||||
let embedded = slf.allocate_visit::<DecoratedStatement>();
|
||||
if !slf.unwinding && slf.peek_token(0).typ == ParseTokenType::left_brace {
|
||||
if !slf.unwinding && slf.peek_token(0).typ == ParseTokenType::LeftBrace {
|
||||
parse_error!(
|
||||
slf,
|
||||
slf.peek_token(0),
|
||||
ParseErrorCode::generic,
|
||||
ParseErrorCode::Generic,
|
||||
"Expected %s, but found %s",
|
||||
token_type_user_presentable_description(
|
||||
ParseTokenType::end,
|
||||
ParseTokenType::End,
|
||||
ParseKeyword::None
|
||||
),
|
||||
slf.peek_token(0).user_presentable_description()
|
||||
@@ -2413,20 +2413,20 @@ fn new_decorated_statement(slf: &mut Populator<'_>) -> Statement {
|
||||
Statement::Decorated(embedded)
|
||||
}
|
||||
|
||||
if self.peek_token(0).typ == ParseTokenType::terminate && self.allow_incomplete() {
|
||||
if self.peek_token(0).typ == ParseTokenType::Terminate && self.allow_incomplete() {
|
||||
// This may happen if we just have a 'time' prefix.
|
||||
// Construct a decorated statement, which will be unsourced.
|
||||
self.allocate_visit::<DecoratedStatement>();
|
||||
} else if self.peek_token(0).typ == ParseTokenType::left_brace {
|
||||
} else if self.peek_token(0).typ == ParseTokenType::LeftBrace {
|
||||
let embedded = self.allocate_boxed_visit::<BraceStatement>();
|
||||
return Statement::Brace(embedded);
|
||||
} else if self.peek_token(0).typ != ParseTokenType::string {
|
||||
} else if self.peek_token(0).typ != ParseTokenType::String {
|
||||
// We may be unwinding already; do not produce another error.
|
||||
// For example in `true | and`.
|
||||
parse_error!(
|
||||
self,
|
||||
self.peek_token(0),
|
||||
ParseErrorCode::generic,
|
||||
ParseErrorCode::Generic,
|
||||
"Expected a command, but found %s",
|
||||
self.peek_token(0).user_presentable_description()
|
||||
);
|
||||
@@ -2444,7 +2444,7 @@ fn new_decorated_statement(slf: &mut Populator<'_>) -> Statement {
|
||||
parse_error!(
|
||||
self,
|
||||
token,
|
||||
ParseErrorCode::bare_variable_assignment,
|
||||
ParseErrorCode::BareVariableAssignment,
|
||||
ERROR_BAD_COMMAND_ASSIGN_ERR_MSG,
|
||||
variable,
|
||||
value
|
||||
@@ -2459,7 +2459,7 @@ fn new_decorated_statement(slf: &mut Populator<'_>) -> Statement {
|
||||
// If we are 'function' or another block starter, then we are a non-block if we are invoked with -h or --help
|
||||
// If we are anything else, we require an argument, so do the same thing if the subsequent
|
||||
// token is a statement terminator.
|
||||
if self.peek_token(0).typ == ParseTokenType::string {
|
||||
if self.peek_token(0).typ == ParseTokenType::String {
|
||||
// If we are one of these, then look for specifically help arguments. Otherwise, if the next token
|
||||
// looks like an option (starts with a dash), then parse it as a decorated statement.
|
||||
let help_only_kws = [
|
||||
@@ -2481,7 +2481,7 @@ fn new_decorated_statement(slf: &mut Populator<'_>) -> Statement {
|
||||
// e.g. a "naked if".
|
||||
let naked_invocation_invokes_help =
|
||||
![ParseKeyword::Begin, ParseKeyword::End].contains(&self.peek_token(0).keyword);
|
||||
if naked_invocation_invokes_help && self.peek_token(1).typ == ParseTokenType::terminate
|
||||
if naked_invocation_invokes_help && self.peek_token(1).typ == ParseTokenType::Terminate
|
||||
{
|
||||
return new_decorated_statement(self);
|
||||
}
|
||||
@@ -2515,7 +2515,7 @@ fn new_decorated_statement(slf: &mut Populator<'_>) -> Statement {
|
||||
parse_error!(
|
||||
self,
|
||||
self.peek_token(0),
|
||||
ParseErrorCode::generic,
|
||||
ParseErrorCode::Generic,
|
||||
"Expected a command, but found %s",
|
||||
self.peek_token(0).user_presentable_description()
|
||||
);
|
||||
@@ -2603,7 +2603,7 @@ fn visit_argument(&mut self, arg: &mut Argument) {
|
||||
arg.range = None;
|
||||
return;
|
||||
}
|
||||
arg.range = Some(self.consume_token_type(ParseTokenType::string));
|
||||
arg.range = Some(self.consume_token_type(ParseTokenType::String));
|
||||
}
|
||||
|
||||
fn visit_variable_assignment(&mut self, varas: &mut VariableAssignment) {
|
||||
@@ -2618,7 +2618,7 @@ fn visit_variable_assignment(&mut self, varas: &mut VariableAssignment) {
|
||||
"Should not have created variable_assignment_t from this token"
|
||||
);
|
||||
}
|
||||
varas.range = Some(self.consume_token_type(ParseTokenType::string));
|
||||
varas.range = Some(self.consume_token_type(ParseTokenType::String));
|
||||
}
|
||||
|
||||
fn visit_job_continuation(&mut self, node: &mut JobContinuation) {
|
||||
@@ -2628,7 +2628,7 @@ fn visit_job_continuation(&mut self, node: &mut JobContinuation) {
|
||||
parse_error!(
|
||||
self,
|
||||
self.peek_token(1),
|
||||
ParseErrorCode::andor_in_pipeline,
|
||||
ParseErrorCode::AndOrInPipeline,
|
||||
INVALID_PIPELINE_CMD_ERR_MSG,
|
||||
kw
|
||||
);
|
||||
@@ -2646,8 +2646,8 @@ fn visit_token(&mut self, token: &mut dyn Token) {
|
||||
if !token.allows_token(self.peek_token(0).typ) {
|
||||
if self.flags.contains(ParseTreeFlags::LEAVE_UNTERMINATED)
|
||||
&& [
|
||||
TokenizerError::unterminated_quote,
|
||||
TokenizerError::unterminated_subshell,
|
||||
TokenizerError::UnterminatedQuote,
|
||||
TokenizerError::UnterminatedSubshell,
|
||||
]
|
||||
.contains(&self.peek_token(0).tok_error)
|
||||
{
|
||||
@@ -2657,7 +2657,7 @@ fn visit_token(&mut self, token: &mut dyn Token) {
|
||||
parse_error!(
|
||||
self,
|
||||
self.peek_token(0),
|
||||
ParseErrorCode::generic,
|
||||
ParseErrorCode::Generic,
|
||||
"Expected %s, but found %s",
|
||||
token_types_user_presentable_description(token.allowed_tokens()),
|
||||
self.peek_token(0).user_presentable_description()
|
||||
@@ -2682,8 +2682,8 @@ fn visit_keyword(&mut self, keyword: &mut dyn Keyword) -> VisitResult {
|
||||
|
||||
if self.flags.contains(ParseTreeFlags::LEAVE_UNTERMINATED)
|
||||
&& [
|
||||
TokenizerError::unterminated_quote,
|
||||
TokenizerError::unterminated_subshell,
|
||||
TokenizerError::UnterminatedQuote,
|
||||
TokenizerError::UnterminatedSubshell,
|
||||
]
|
||||
.contains(&self.peek_token(0).tok_error)
|
||||
{
|
||||
@@ -2701,7 +2701,7 @@ fn visit_keyword(&mut self, keyword: &mut dyn Keyword) -> VisitResult {
|
||||
parse_error!(
|
||||
self,
|
||||
self.peek_token(0),
|
||||
ParseErrorCode::generic,
|
||||
ParseErrorCode::Generic,
|
||||
"Expected %s, but found %s",
|
||||
keywords_user_presentable_description(allowed_keywords),
|
||||
self.peek_token(0).user_presentable_description(),
|
||||
@@ -2724,7 +2724,7 @@ fn visit_maybe_newlines(&mut self, nls: &mut MaybeNewlines) {
|
||||
// TODO: it would be nice to have the start offset be the current position in the token
|
||||
// stream, even if there are no newlines.
|
||||
while self.peek_token(0).is_newline {
|
||||
let r = self.consume_token_type(ParseTokenType::end);
|
||||
let r = self.consume_token_type(ParseTokenType::End);
|
||||
if range.length == 0 {
|
||||
range = r;
|
||||
} else {
|
||||
@@ -2772,17 +2772,17 @@ fn from(flags: ParseTreeFlags) -> Self {
|
||||
impl From<TokenType> for ParseTokenType {
|
||||
fn from(token_type: TokenType) -> Self {
|
||||
match token_type {
|
||||
TokenType::string => ParseTokenType::string,
|
||||
TokenType::pipe => ParseTokenType::pipe,
|
||||
TokenType::andand => ParseTokenType::andand,
|
||||
TokenType::oror => ParseTokenType::oror,
|
||||
TokenType::end => ParseTokenType::end,
|
||||
TokenType::background => ParseTokenType::background,
|
||||
TokenType::left_brace => ParseTokenType::left_brace,
|
||||
TokenType::right_brace => ParseTokenType::right_brace,
|
||||
TokenType::redirect => ParseTokenType::redirection,
|
||||
TokenType::error => ParseTokenType::tokenizer_error,
|
||||
TokenType::comment => ParseTokenType::comment,
|
||||
TokenType::String => ParseTokenType::String,
|
||||
TokenType::Pipe => ParseTokenType::Pipe,
|
||||
TokenType::AndAnd => ParseTokenType::AndAnd,
|
||||
TokenType::OrOr => ParseTokenType::OrOr,
|
||||
TokenType::End => ParseTokenType::End,
|
||||
TokenType::Background => ParseTokenType::Background,
|
||||
TokenType::LeftBrace => ParseTokenType::LeftBrace,
|
||||
TokenType::RightBrace => ParseTokenType::RightBrace,
|
||||
TokenType::Redirect => ParseTokenType::Redirection,
|
||||
TokenType::Error => ParseTokenType::TokenizerError,
|
||||
TokenType::Comment => ParseTokenType::Comment,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2794,7 +2794,7 @@ fn is_keyword_char(c: char) -> bool {
|
||||
/// Given a token, returns unescaped keyword, or the empty string.
|
||||
pub(crate) fn unescape_keyword(tok: TokenType, token: &wstr) -> Cow<'_, wstr> {
|
||||
/* Only strings can be keywords */
|
||||
if tok != TokenType::string {
|
||||
if tok != TokenType::String {
|
||||
return Cow::Borrowed(L!(""));
|
||||
}
|
||||
|
||||
|
||||
@@ -220,11 +220,11 @@ fn write_part(
|
||||
break;
|
||||
}
|
||||
let is_redirection_target = in_redirection;
|
||||
in_redirection = token.type_ == TokenType::redirect;
|
||||
if is_redirection_target && token.type_ == TokenType::string {
|
||||
in_redirection = token.type_ == TokenType::Redirect;
|
||||
if is_redirection_target && token.type_ == TokenType::String {
|
||||
continue;
|
||||
}
|
||||
if token.type_ != TokenType::string {
|
||||
if token.type_ != TokenType::String {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -382,10 +382,10 @@ fn gap_text_flags_before_node(&self, node: &dyn Node) -> GapFlags {
|
||||
Kind::Token(token) => {
|
||||
// Allow escaped newlines before && and ||, and also pipes.
|
||||
match token.token_type() {
|
||||
ParseTokenType::andand | ParseTokenType::oror | ParseTokenType::pipe => {
|
||||
ParseTokenType::AndAnd | ParseTokenType::OrOr | ParseTokenType::Pipe => {
|
||||
result.allow_escaped_newlines = true;
|
||||
}
|
||||
ParseTokenType::string => {
|
||||
ParseTokenType::String => {
|
||||
// Allow escaped newlines before commands that follow a variable assignment
|
||||
// since both can be long (#7955).
|
||||
let p = self.traversal.parent(node);
|
||||
@@ -531,17 +531,17 @@ fn emit_gap_text(&mut self, range: SourceRange, flags: GapFlags) -> bool {
|
||||
}
|
||||
} else if self.gap_text_mask_newline {
|
||||
// When told to mask newlines, we do it as long as we get semicolon or newline.
|
||||
if tok.type_ == TokenType::end {
|
||||
if tok.type_ == TokenType::End {
|
||||
continue;
|
||||
}
|
||||
self.gap_text_mask_newline = false;
|
||||
}
|
||||
|
||||
if tok.type_ == TokenType::comment {
|
||||
if tok.type_ == TokenType::Comment {
|
||||
self.emit_space_or_indent(GapFlags::default());
|
||||
self.output.push_utfstr(tok_text);
|
||||
needs_nl = true;
|
||||
} else if tok.type_ == TokenType::end {
|
||||
} else if tok.type_ == TokenType::End {
|
||||
// This may be either a newline or semicolon.
|
||||
// Semicolons found here are not part of the ast and can simply be removed.
|
||||
// Newlines are preserved unless mask_newline is set.
|
||||
@@ -819,9 +819,9 @@ fn prettify_traversal(&mut self) {
|
||||
}
|
||||
if let Some(token) = node.as_token() {
|
||||
match token.token_type() {
|
||||
ParseTokenType::end => self.visit_semi_nl(token),
|
||||
ParseTokenType::left_brace => self.visit_left_brace(token),
|
||||
ParseTokenType::right_brace => self.visit_right_brace(token),
|
||||
ParseTokenType::End => self.visit_semi_nl(token),
|
||||
ParseTokenType::LeftBrace => self.visit_left_brace(token),
|
||||
ParseTokenType::RightBrace => self.visit_right_brace(token),
|
||||
_ => self.emit_node_text(node),
|
||||
}
|
||||
continue;
|
||||
|
||||
@@ -486,7 +486,7 @@ fn plain() {
|
||||
#[serial]
|
||||
#[rustfmt::skip]
|
||||
fn test_qmark_noglob_true() {
|
||||
scoped_test(FeatureFlag::qmark_noglob, true, || {
|
||||
scoped_test(FeatureFlag::QuestionMarkNoGlob, true, || {
|
||||
validate!(["string", "match", "a*b?c", "axxb?c"], STATUS_CMD_OK, "axxb?c\n");
|
||||
validate!(["string", "match", "*?", "a"], STATUS_CMD_ERROR, "");
|
||||
validate!(["string", "match", "*?", "ab"], STATUS_CMD_ERROR, "");
|
||||
@@ -514,7 +514,7 @@ fn test_qmark_noglob_true() {
|
||||
#[serial]
|
||||
#[rustfmt::skip]
|
||||
fn test_qmark_glob() {
|
||||
scoped_test(FeatureFlag::qmark_noglob, false, || {
|
||||
scoped_test(FeatureFlag::QuestionMarkNoGlob, false, || {
|
||||
validate!(["string", "match", "a*b?c", "axxbyc"], STATUS_CMD_OK, "axxbyc\n");
|
||||
validate!(["string", "match", "*?", "a"], STATUS_CMD_OK, "a\n");
|
||||
validate!(["string", "match", "*?", "ab"], STATUS_CMD_OK, "ab\n");
|
||||
|
||||
@@ -190,7 +190,7 @@ fn new(
|
||||
.build(pattern.as_char_slice())
|
||||
.map_err(|e| RegexError::Compile(pattern.to_owned(), e))?;
|
||||
|
||||
let replacement = if feature_test(FeatureFlag::string_replace_backslash) {
|
||||
let replacement = if feature_test(FeatureFlag::StringReplaceBackslash) {
|
||||
replacement.to_owned()
|
||||
} else {
|
||||
Self::interpret_escape(replacement)
|
||||
|
||||
@@ -551,7 +551,7 @@ fn parse_just_a_string(&mut self, start: usize, end: usize) -> Option<Box<dyn Ex
|
||||
);
|
||||
}
|
||||
|
||||
if feature_test(FeatureFlag::test_require_arg) {
|
||||
if feature_test(FeatureFlag::TestRequireArg) {
|
||||
return self.error(start, sprintf!("Unknown option at index %u", start));
|
||||
}
|
||||
|
||||
@@ -1019,7 +1019,7 @@ pub fn test(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Bui
|
||||
.collect();
|
||||
let args: &[WString] = &args;
|
||||
|
||||
if feature_test(FeatureFlag::test_require_arg) {
|
||||
if feature_test(FeatureFlag::TestRequireArg) {
|
||||
if argc == 0 {
|
||||
streams.err.appendln(wgettext_fmt!(
|
||||
"%s: Expected at least one argument",
|
||||
|
||||
@@ -184,7 +184,7 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
|
||||
let escape_comma = flags.contains(EscapeFlags::COMMA);
|
||||
let no_quoted = flags.contains(EscapeFlags::NO_QUOTED);
|
||||
let no_tilde = flags.contains(EscapeFlags::NO_TILDE);
|
||||
let no_qmark = feature_test(FeatureFlag::qmark_noglob);
|
||||
let no_qmark = feature_test(FeatureFlag::QuestionMarkNoGlob);
|
||||
let symbolic = flags.contains(EscapeFlags::SYMBOLIC);
|
||||
|
||||
assert!(
|
||||
@@ -501,7 +501,7 @@ fn unescape_string_internal(input: &wstr, flags: UnescapeFlags) -> Option<WStrin
|
||||
let unescape_special = flags.contains(UnescapeFlags::SPECIAL);
|
||||
let allow_incomplete = flags.contains(UnescapeFlags::INCOMPLETE);
|
||||
let ignore_backslashes = flags.contains(UnescapeFlags::NO_BACKSLASHES);
|
||||
let allow_percent_self = !feature_test(FeatureFlag::remove_percent_self);
|
||||
let allow_percent_self = !feature_test(FeatureFlag::RemovePercentSelf);
|
||||
|
||||
// The positions of open braces.
|
||||
let mut braces = vec![];
|
||||
@@ -583,7 +583,7 @@ enum Mode {
|
||||
}
|
||||
}
|
||||
'?' => {
|
||||
if unescape_special && !feature_test(FeatureFlag::qmark_noglob) {
|
||||
if unescape_special && !feature_test(FeatureFlag::QuestionMarkNoGlob) {
|
||||
to_append_or_none = Some(ANY_CHAR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
cmp::Ordering,
|
||||
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::{
|
||||
Mutex, MutexGuard,
|
||||
atomic::{self, AtomicUsize},
|
||||
@@ -246,7 +247,7 @@ pub struct CompletionReceiver {
|
||||
|
||||
// We are only wrapping a `Vec<Completion>`, any non-mutable methods can be safely deferred to the
|
||||
// Vec-impl
|
||||
impl std::ops::Deref for CompletionReceiver {
|
||||
impl Deref for CompletionReceiver {
|
||||
type Target = [Completion];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@@ -254,7 +255,7 @@ fn deref(&self) -> &Self::Target {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for CompletionReceiver {
|
||||
impl DerefMut for CompletionReceiver {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.completions.as_mut_slice()
|
||||
}
|
||||
@@ -705,25 +706,25 @@ fn perform_for_commandline_impl(&mut self, cmdline: WString) {
|
||||
&cmdline[first_token.offset()..]
|
||||
};
|
||||
|
||||
if tokens.last().unwrap().type_ == TokenType::comment {
|
||||
if tokens.last().unwrap().type_ == TokenType::Comment {
|
||||
return;
|
||||
}
|
||||
tokens.retain(|tok| tok.type_ != TokenType::comment);
|
||||
tokens.retain(|tok| tok.type_ != TokenType::Comment);
|
||||
assert!(!tokens.is_empty());
|
||||
|
||||
let cmd_tok = tokens.first().unwrap();
|
||||
let cur_tok = tokens.last().unwrap();
|
||||
|
||||
// Since fish does not currently support redirect in command position, we return here.
|
||||
if cmd_tok.type_ != TokenType::string {
|
||||
if cmd_tok.type_ != TokenType::String {
|
||||
return;
|
||||
}
|
||||
if cur_tok.type_ == TokenType::error {
|
||||
if cur_tok.type_ == TokenType::Error {
|
||||
return;
|
||||
}
|
||||
for tok in &tokens {
|
||||
// If there was an error, it was in the last token.
|
||||
assert!(matches!(tok.type_, TokenType::string | TokenType::redirect));
|
||||
assert!(matches!(tok.type_, TokenType::String | TokenType::Redirect));
|
||||
}
|
||||
// If we are completing a variable name or a tilde expansion user name, we do that and
|
||||
// return. No need for any other completions.
|
||||
@@ -756,12 +757,12 @@ fn perform_for_commandline_impl(&mut self, cmdline: WString) {
|
||||
return;
|
||||
}
|
||||
// See whether we are in an argument, in a redirection or in the whitespace in between.
|
||||
let mut in_redirection = cur_tok.type_ == TokenType::redirect;
|
||||
let mut in_redirection = cur_tok.type_ == TokenType::Redirect;
|
||||
|
||||
let mut had_ddash = false;
|
||||
let mut current_argument = L!("");
|
||||
let mut previous_argument = L!("");
|
||||
if cur_tok.type_ == TokenType::string
|
||||
if cur_tok.type_ == TokenType::String
|
||||
&& cur_tok.location_in_or_at_end_of_source_range(position_in_statement)
|
||||
{
|
||||
// If the cursor is in whitespace, then the "current" argument is empty and the
|
||||
@@ -775,10 +776,10 @@ fn perform_for_commandline_impl(&mut self, cmdline: WString) {
|
||||
current_argument = current_token;
|
||||
if tokens.len() >= 2 {
|
||||
let prev_tok = &tokens[tokens.len() - 2];
|
||||
if prev_tok.type_ == TokenType::string {
|
||||
if prev_tok.type_ == TokenType::String {
|
||||
previous_argument = prev_tok.get_source(&cmdline);
|
||||
}
|
||||
in_redirection = prev_tok.type_ == TokenType::redirect;
|
||||
in_redirection = prev_tok.type_ == TokenType::Redirect;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,9 +30,9 @@ pub struct CallbackData {
|
||||
// List of fish universal variable formats.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum UvarFormat {
|
||||
fish_2_x,
|
||||
fish_3_0,
|
||||
future,
|
||||
Fish_2_x,
|
||||
Fish_3_0,
|
||||
Future,
|
||||
}
|
||||
|
||||
/// Class representing universal variables.
|
||||
@@ -250,14 +250,14 @@ fn populate_variables(s: &[u8], out_vars: &mut VarTable) -> UvarFormat {
|
||||
wide_line = WString::from_str(line);
|
||||
|
||||
match format {
|
||||
UvarFormat::fish_2_x => {
|
||||
UvarFormat::Fish_2_x => {
|
||||
Self::parse_message_2x_internal(&wide_line, out_vars, &mut storage);
|
||||
}
|
||||
UvarFormat::fish_3_0 => {
|
||||
UvarFormat::Fish_3_0 => {
|
||||
Self::parse_message_30_internal(&wide_line, out_vars, &mut storage);
|
||||
}
|
||||
// For future formats, just try with the most recent one.
|
||||
UvarFormat::future => {
|
||||
UvarFormat::Future => {
|
||||
Self::parse_message_30_internal(&wide_line, out_vars, &mut storage);
|
||||
}
|
||||
}
|
||||
@@ -298,13 +298,13 @@ fn format_for_contents(s: &[u8]) -> UvarFormat {
|
||||
return if versionbuf.starts_with(UVARS_VERSION_3_0)
|
||||
&& versionbuf[UVARS_VERSION_3_0.len()] == b'\0'
|
||||
{
|
||||
UvarFormat::fish_3_0
|
||||
UvarFormat::Fish_3_0
|
||||
} else {
|
||||
UvarFormat::future
|
||||
UvarFormat::Future
|
||||
};
|
||||
}
|
||||
// No version found, assume 2.x
|
||||
return UvarFormat::fish_2_x;
|
||||
return UvarFormat::Fish_2_x;
|
||||
}
|
||||
|
||||
/// Serialize a variable list.
|
||||
@@ -417,7 +417,7 @@ fn load_from_file(
|
||||
|
||||
// Hacky: if the read format is in the future, avoid overwriting the file: never try to
|
||||
// save.
|
||||
let do_save = format != UvarFormat::future;
|
||||
let do_save = format != UvarFormat::Future;
|
||||
|
||||
// Announce changes and update our exports generation.
|
||||
let (export_generation_increment, callbacks) =
|
||||
@@ -1110,14 +1110,14 @@ macro_rules! validate {
|
||||
);
|
||||
};
|
||||
}
|
||||
validate!(b"# VERSION: 3.0", UvarFormat::fish_3_0);
|
||||
validate!(b"# version: 3.0", UvarFormat::fish_2_x);
|
||||
validate!(b"# blah blahVERSION: 3.0", UvarFormat::fish_2_x);
|
||||
validate!(b"stuff\n# blah blahVERSION: 3.0", UvarFormat::fish_2_x);
|
||||
validate!(b"# blah\n# VERSION: 3.0", UvarFormat::fish_3_0);
|
||||
validate!(b"# blah\n#VERSION: 3.0", UvarFormat::fish_3_0);
|
||||
validate!(b"# blah\n#VERSION:3.0", UvarFormat::fish_3_0);
|
||||
validate!(b"# blah\n#VERSION:3.1", UvarFormat::future);
|
||||
validate!(b"# VERSION: 3.0", UvarFormat::Fish_3_0);
|
||||
validate!(b"# version: 3.0", UvarFormat::Fish_2_x);
|
||||
validate!(b"# blah blahVERSION: 3.0", UvarFormat::Fish_2_x);
|
||||
validate!(b"stuff\n# blah blahVERSION: 3.0", UvarFormat::Fish_2_x);
|
||||
validate!(b"# blah\n# VERSION: 3.0", UvarFormat::Fish_3_0);
|
||||
validate!(b"# blah\n#VERSION: 3.0", UvarFormat::Fish_3_0);
|
||||
validate!(b"# blah\n#VERSION:3.0", UvarFormat::Fish_3_0);
|
||||
validate!(b"# blah\n#VERSION:3.1", UvarFormat::Future);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
32
src/event.rs
32
src/event.rs
@@ -17,14 +17,14 @@
|
||||
use crate::signal::{Signal, signal_check_cancel, signal_handle};
|
||||
use crate::wchar::prelude::*;
|
||||
|
||||
pub enum event_type_t {
|
||||
any,
|
||||
signal,
|
||||
variable,
|
||||
process_exit,
|
||||
job_exit,
|
||||
caller_exit,
|
||||
generic,
|
||||
pub enum EventType {
|
||||
Any,
|
||||
Signal,
|
||||
Variable,
|
||||
ProcessExit,
|
||||
JobExit,
|
||||
CallerExit,
|
||||
Generic,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
@@ -105,16 +105,16 @@ fn matches_filter(&self, filter: &wstr) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&EventDescription> for event_type_t {
|
||||
impl From<&EventDescription> for EventType {
|
||||
fn from(desc: &EventDescription) -> Self {
|
||||
match desc {
|
||||
EventDescription::Any => event_type_t::any,
|
||||
EventDescription::Signal { .. } => event_type_t::signal,
|
||||
EventDescription::Variable { .. } => event_type_t::variable,
|
||||
EventDescription::ProcessExit { .. } => event_type_t::process_exit,
|
||||
EventDescription::JobExit { .. } => event_type_t::job_exit,
|
||||
EventDescription::CallerExit { .. } => event_type_t::caller_exit,
|
||||
EventDescription::Generic { .. } => event_type_t::generic,
|
||||
EventDescription::Any => EventType::Any,
|
||||
EventDescription::Signal { .. } => EventType::Signal,
|
||||
EventDescription::Variable { .. } => EventType::Variable,
|
||||
EventDescription::ProcessExit { .. } => EventType::ProcessExit,
|
||||
EventDescription::JobExit { .. } => EventType::JobExit,
|
||||
EventDescription::CallerExit { .. } => EventType::CallerExit,
|
||||
EventDescription::Generic { .. } => EventType::Generic,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
src/exec.rs
16
src/exec.rs
@@ -790,21 +790,21 @@ fn create_output_stream_for_builtin(
|
||||
return OutputStream::Fd(FdOutputStream::new(fd));
|
||||
};
|
||||
match io.io_mode() {
|
||||
IoMode::bufferfill => {
|
||||
IoMode::BufferFill => {
|
||||
// Our IO redirection is to an internal buffer, e.g. a command substitution.
|
||||
// We will write directly to it.
|
||||
let buffer = io.as_bufferfill().unwrap().buffer();
|
||||
OutputStream::Buffered(BufferedOutputStream::new(buffer.clone()))
|
||||
}
|
||||
IoMode::close => {
|
||||
IoMode::Close => {
|
||||
// Like 'echo foo >&-'
|
||||
OutputStream::Null
|
||||
}
|
||||
IoMode::file => {
|
||||
IoMode::File => {
|
||||
// Output is to a file which has been opened.
|
||||
OutputStream::Fd(FdOutputStream::new(io.source_fd()))
|
||||
}
|
||||
IoMode::pipe => {
|
||||
IoMode::Pipe => {
|
||||
// Output is to a pipe. We may need to buffer.
|
||||
if piped_output_needs_buffering {
|
||||
OutputStream::String(StringOutputStream::new())
|
||||
@@ -812,7 +812,7 @@ fn create_output_stream_for_builtin(
|
||||
OutputStream::Fd(FdOutputStream::new(io.source_fd()))
|
||||
}
|
||||
}
|
||||
IoMode::fd => {
|
||||
IoMode::Fd => {
|
||||
// This is a case like 'echo foo >&5'
|
||||
// It's uncommon and unclear what should happen.
|
||||
OutputStream::String(StringOutputStream::new())
|
||||
@@ -1158,7 +1158,7 @@ fn get_performer_for_builtin(p: &Process, j: &Job, io_chain: &IoChain) -> Box<Pr
|
||||
// which is internal to fish. We still respect this redirection in
|
||||
// that we pass it on as a block IO to the code that source runs,
|
||||
// and therefore this is not an error.
|
||||
let ignore_redirect = inp.io_mode() == IoMode::fd && inp.source_fd() >= 3;
|
||||
let ignore_redirect = inp.io_mode() == IoMode::Fd && inp.source_fd() >= 3;
|
||||
if !ignore_redirect {
|
||||
local_builtin_stdin = inp.source_fd();
|
||||
}
|
||||
@@ -1171,8 +1171,8 @@ fn get_performer_for_builtin(p: &Process, j: &Job, io_chain: &IoChain) -> Box<Pr
|
||||
streams.stdin_is_directly_redirected = stdin_is_directly_redirected;
|
||||
streams.out_is_redirected = out_io.is_some();
|
||||
streams.err_is_redirected = err_io.is_some();
|
||||
streams.out_is_piped = out_io.is_some_and(|io| io.io_mode() == IoMode::pipe);
|
||||
streams.err_is_piped = err_io.is_some_and(|io| io.io_mode() == IoMode::pipe);
|
||||
streams.out_is_piped = out_io.is_some_and(|io| io.io_mode() == IoMode::Pipe);
|
||||
streams.err_is_piped = err_io.is_some_and(|io| io.io_mode() == IoMode::Pipe);
|
||||
|
||||
// Disallow nul bytes in the arguments, as they are not allowed in builtins.
|
||||
let mut shim_argv: Vec<&wstr> =
|
||||
|
||||
@@ -360,7 +360,7 @@ macro_rules! append_syntax_error {
|
||||
let mut error = ParseError::default();
|
||||
error.source_start = $source_start;
|
||||
error.source_length = 0;
|
||||
error.code = ParseErrorCode::syntax;
|
||||
error.code = ParseErrorCode::Syntax;
|
||||
error.text = wgettext_fmt!($fmt $(, $arg)*);
|
||||
errors.push(error);
|
||||
}
|
||||
@@ -390,7 +390,7 @@ macro_rules! append_cmdsub_error_formatted {
|
||||
let mut error = ParseError::default();
|
||||
error.source_start = $source_start;
|
||||
error.source_length = $source_end - $source_start + 1;
|
||||
error.code = ParseErrorCode::cmdsubst;
|
||||
error.code = ParseErrorCode::CmdSubst;
|
||||
error.text = $text;
|
||||
if !errors.iter().any(|e| e.text == error.text) {
|
||||
errors.push(error);
|
||||
@@ -408,7 +408,7 @@ fn append_overflow_error(
|
||||
errors.push(ParseError {
|
||||
source_start: source_start.unwrap_or(SOURCE_LOCATION_UNKNOWN),
|
||||
source_length: 0,
|
||||
code: ParseErrorCode::generic,
|
||||
code: ParseErrorCode::Generic,
|
||||
text: wgettext!("Expansion produced too many results").to_owned(),
|
||||
});
|
||||
}
|
||||
@@ -1395,7 +1395,7 @@ fn stage_home_and_self(
|
||||
out: &mut CompletionReceiver,
|
||||
) -> ExpandResult {
|
||||
expand_home_directory(&mut input, self.ctx.vars());
|
||||
if !feature_test(FeatureFlag::remove_percent_self) {
|
||||
if !feature_test(FeatureFlag::RemovePercentSelf) {
|
||||
expand_percent_self(&mut input);
|
||||
}
|
||||
if !out.add(input) {
|
||||
|
||||
@@ -154,7 +154,7 @@ pub fn make_autoclose_pipes() -> nix::Result<AutoClosePipes> {
|
||||
let pipes = match nix::unistd::pipe() {
|
||||
Ok(pipes) => pipes,
|
||||
Err(err) => {
|
||||
FLOG!(warning, PIPE_ERROR.localize());
|
||||
FLOG!(WARNING, PIPE_ERROR.localize());
|
||||
perror("pipe");
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ pub mod categories {
|
||||
use crate::wchar::prelude::*;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
pub struct category_t {
|
||||
pub struct Category {
|
||||
pub name: &'static wstr,
|
||||
pub description: LocalizableString,
|
||||
pub enabled: AtomicBool,
|
||||
@@ -24,7 +24,7 @@ macro_rules! declare_category {
|
||||
(
|
||||
($var:ident, $name:literal, $description:literal, $enabled:expr)
|
||||
) => {
|
||||
pub static $var: category_t = category_t {
|
||||
pub static $var: Category = Category {
|
||||
name: L!($name),
|
||||
description: localizable_string!($description),
|
||||
enabled: AtomicBool::new($enabled),
|
||||
@@ -61,7 +61,7 @@ macro_rules! categories {
|
||||
)*
|
||||
|
||||
// Define a function which gives you a Vector of all categories.
|
||||
pub fn all_categories() -> Vec<&'static category_t> {
|
||||
pub fn all_categories() -> Vec<&'static Category> {
|
||||
vec![
|
||||
$(
|
||||
& category_name!($cats),
|
||||
|
||||
@@ -114,7 +114,7 @@ impl LockedFile {
|
||||
pub fn new(locking_mode: LockingMode, file_path: &wstr) -> std::io::Result<Self> {
|
||||
let dir_path = wdirname(file_path);
|
||||
|
||||
if path_remoteness(dir_path) == DirRemoteness::remote {
|
||||
if path_remoteness(dir_path) == DirRemoteness::Remote {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Unsupported,
|
||||
"Directory considered remote. Locking is disabled on remote file systems.",
|
||||
|
||||
@@ -12,33 +12,33 @@
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum FeatureFlag {
|
||||
/// Whether ^ is supported for stderr redirection.
|
||||
stderr_nocaret,
|
||||
StderrNoCaret,
|
||||
|
||||
/// Whether ? is supported as a glob.
|
||||
qmark_noglob,
|
||||
QuestionMarkNoGlob,
|
||||
|
||||
/// Whether string replace -r double-unescapes the replacement.
|
||||
string_replace_backslash,
|
||||
StringReplaceBackslash,
|
||||
|
||||
/// Whether "&" is not-special if followed by a word character.
|
||||
ampersand_nobg_in_token,
|
||||
AmpersandNoBgInToken,
|
||||
/// Whether "%self" is expanded to fish's pid
|
||||
remove_percent_self,
|
||||
RemovePercentSelf,
|
||||
|
||||
/// Remove `test`'s one and zero arg mode (make `test -n` return false etc)
|
||||
test_require_arg,
|
||||
TestRequireArg,
|
||||
|
||||
/// Whether to write OSC 133 prompt markers
|
||||
mark_prompt,
|
||||
MarkPrompt,
|
||||
|
||||
/// Do not look up $TERM in terminfo database.
|
||||
ignore_terminfo,
|
||||
IgnoreTerminfo,
|
||||
|
||||
/// Whether we are allowed to query the TTY for extra information.
|
||||
query_term,
|
||||
QueryTerm,
|
||||
|
||||
/// Do not try to work around incompatible terminal.
|
||||
omit_term_workarounds,
|
||||
OmitTermWorkarounds,
|
||||
}
|
||||
|
||||
struct Features {
|
||||
@@ -72,7 +72,7 @@ pub struct FeatureMetadata {
|
||||
/// The metadata, indexed by flag.
|
||||
pub const METADATA: &[FeatureMetadata] = &[
|
||||
FeatureMetadata {
|
||||
flag: FeatureFlag::stderr_nocaret,
|
||||
flag: FeatureFlag::StderrNoCaret,
|
||||
name: L!("stderr-nocaret"),
|
||||
groups: L!("3.0"),
|
||||
description: L!("^ no longer redirects stderr (historical, can no longer be changed)"),
|
||||
@@ -80,7 +80,7 @@ pub struct FeatureMetadata {
|
||||
read_only: true,
|
||||
},
|
||||
FeatureMetadata {
|
||||
flag: FeatureFlag::qmark_noglob,
|
||||
flag: FeatureFlag::QuestionMarkNoGlob,
|
||||
name: L!("qmark-noglob"),
|
||||
groups: L!("3.0"),
|
||||
description: L!("? no longer globs"),
|
||||
@@ -88,7 +88,7 @@ pub struct FeatureMetadata {
|
||||
read_only: false,
|
||||
},
|
||||
FeatureMetadata {
|
||||
flag: FeatureFlag::string_replace_backslash,
|
||||
flag: FeatureFlag::StringReplaceBackslash,
|
||||
name: L!("regex-easyesc"),
|
||||
groups: L!("3.1"),
|
||||
description: L!("string replace -r needs fewer \\'s"),
|
||||
@@ -96,7 +96,7 @@ pub struct FeatureMetadata {
|
||||
read_only: false,
|
||||
},
|
||||
FeatureMetadata {
|
||||
flag: FeatureFlag::ampersand_nobg_in_token,
|
||||
flag: FeatureFlag::AmpersandNoBgInToken,
|
||||
name: L!("ampersand-nobg-in-token"),
|
||||
groups: L!("3.4"),
|
||||
description: L!("& only backgrounds if followed by a separator"),
|
||||
@@ -104,7 +104,7 @@ pub struct FeatureMetadata {
|
||||
read_only: false,
|
||||
},
|
||||
FeatureMetadata {
|
||||
flag: FeatureFlag::remove_percent_self,
|
||||
flag: FeatureFlag::RemovePercentSelf,
|
||||
name: L!("remove-percent-self"),
|
||||
groups: L!("4.0"),
|
||||
description: L!("%self is no longer expanded (use $fish_pid)"),
|
||||
@@ -112,7 +112,7 @@ pub struct FeatureMetadata {
|
||||
read_only: false,
|
||||
},
|
||||
FeatureMetadata {
|
||||
flag: FeatureFlag::test_require_arg,
|
||||
flag: FeatureFlag::TestRequireArg,
|
||||
name: L!("test-require-arg"),
|
||||
groups: L!("4.0"),
|
||||
description: L!("builtin test requires an argument"),
|
||||
@@ -120,7 +120,7 @@ pub struct FeatureMetadata {
|
||||
read_only: false,
|
||||
},
|
||||
FeatureMetadata {
|
||||
flag: FeatureFlag::mark_prompt,
|
||||
flag: FeatureFlag::MarkPrompt,
|
||||
name: L!("mark-prompt"),
|
||||
groups: L!("4.0"),
|
||||
description: L!("write OSC 133 prompt markers to the terminal"),
|
||||
@@ -128,7 +128,7 @@ pub struct FeatureMetadata {
|
||||
read_only: false,
|
||||
},
|
||||
FeatureMetadata {
|
||||
flag: FeatureFlag::ignore_terminfo,
|
||||
flag: FeatureFlag::IgnoreTerminfo,
|
||||
name: L!("ignore-terminfo"),
|
||||
groups: L!("4.1"),
|
||||
description: L!("do not look up $TERM in terminfo database"),
|
||||
@@ -136,7 +136,7 @@ pub struct FeatureMetadata {
|
||||
read_only: false,
|
||||
},
|
||||
FeatureMetadata {
|
||||
flag: FeatureFlag::query_term,
|
||||
flag: FeatureFlag::QueryTerm,
|
||||
name: L!("query-term"),
|
||||
groups: L!("4.1"),
|
||||
description: L!("query the TTY to enable extra functionality"),
|
||||
@@ -144,7 +144,7 @@ pub struct FeatureMetadata {
|
||||
read_only: false,
|
||||
},
|
||||
FeatureMetadata {
|
||||
flag: FeatureFlag::omit_term_workarounds,
|
||||
flag: FeatureFlag::OmitTermWorkarounds,
|
||||
name: L!("omit-term-workarounds"),
|
||||
groups: L!("4.3"),
|
||||
description: L!("skip workarounds for incompatible terminals"),
|
||||
@@ -294,9 +294,9 @@ mod tests {
|
||||
fn test_feature_flags() {
|
||||
let f = Features::new();
|
||||
f.set_from_string(L!("stderr-nocaret,nonsense"));
|
||||
assert!(f.test(FeatureFlag::stderr_nocaret));
|
||||
assert!(f.test(FeatureFlag::StderrNoCaret));
|
||||
f.set_from_string(L!("stderr-nocaret,no-stderr-nocaret,nonsense"));
|
||||
assert!(f.test(FeatureFlag::stderr_nocaret));
|
||||
assert!(f.test(FeatureFlag::StderrNoCaret));
|
||||
|
||||
// Ensure every metadata is represented once.
|
||||
let mut counts: [usize; METADATA.len()] = [0; METADATA.len()];
|
||||
@@ -308,31 +308,31 @@ fn test_feature_flags() {
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
METADATA[FeatureFlag::stderr_nocaret as usize].name,
|
||||
METADATA[FeatureFlag::StderrNoCaret as usize].name,
|
||||
L!("stderr-nocaret")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scoped() {
|
||||
scoped_test(FeatureFlag::qmark_noglob, true, || {
|
||||
assert!(test(FeatureFlag::qmark_noglob));
|
||||
scoped_test(FeatureFlag::QuestionMarkNoGlob, true, || {
|
||||
assert!(test(FeatureFlag::QuestionMarkNoGlob));
|
||||
});
|
||||
|
||||
set(FeatureFlag::qmark_noglob, true);
|
||||
set(FeatureFlag::QuestionMarkNoGlob, true);
|
||||
|
||||
scoped_test(FeatureFlag::qmark_noglob, false, || {
|
||||
assert!(!test(FeatureFlag::qmark_noglob));
|
||||
scoped_test(FeatureFlag::QuestionMarkNoGlob, false, || {
|
||||
assert!(!test(FeatureFlag::QuestionMarkNoGlob));
|
||||
});
|
||||
|
||||
set(FeatureFlag::qmark_noglob, false);
|
||||
set(FeatureFlag::QuestionMarkNoGlob, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_nested_scopes_not_supported() {
|
||||
scoped_test(FeatureFlag::qmark_noglob, true, || {
|
||||
scoped_test(FeatureFlag::qmark_noglob, false, || {});
|
||||
scoped_test(FeatureFlag::QuestionMarkNoGlob, true, || {
|
||||
scoped_test(FeatureFlag::QuestionMarkNoGlob, false, || {});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ pub fn test_redirection_target(&self, target: &wstr, mode: RedirectionMode) -> b
|
||||
// redirections). Note that the target is now unescaped.
|
||||
let target_path = path_apply_working_directory(&target, &self.working_directory);
|
||||
match mode {
|
||||
RedirectionMode::fd => {
|
||||
RedirectionMode::Fd => {
|
||||
if target == "-" {
|
||||
return true;
|
||||
}
|
||||
@@ -156,14 +156,14 @@ pub fn test_redirection_target(&self, target: &wstr, mode: RedirectionMode) -> b
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
RedirectionMode::input | RedirectionMode::try_input => {
|
||||
RedirectionMode::Input | RedirectionMode::TryInput => {
|
||||
// Input redirections must have a readable non-directory.
|
||||
// Note we color "try_input" files as errors if they are invalid,
|
||||
// even though it's possible to execute these (replaced via /dev/null).
|
||||
waccess(&target_path, libc::R_OK) == 0
|
||||
&& wstat(&target_path).is_ok_and(|md| !md.file_type().is_dir())
|
||||
}
|
||||
RedirectionMode::overwrite | RedirectionMode::append | RedirectionMode::noclob => {
|
||||
RedirectionMode::Overwrite | RedirectionMode::Append | RedirectionMode::NoClob => {
|
||||
if string_suffixes_string(L!("/"), &target) {
|
||||
// Redirections to things that are directories is definitely not
|
||||
// allowed.
|
||||
@@ -206,8 +206,8 @@ pub fn test_redirection_target(&self, target: &wstr, mode: RedirectionMode) -> b
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOCLOB means that we must not overwrite files that exist.
|
||||
file_is_writable && !(file_exists && mode == RedirectionMode::noclob)
|
||||
// NoClob means that we must not overwrite files that exist.
|
||||
file_is_writable && !(file_exists && mode == RedirectionMode::NoClob)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -546,63 +546,63 @@ fn test_redirections() {
|
||||
create_dir_all(&dir_path).unwrap();
|
||||
|
||||
// Normal redirection.
|
||||
let result = tester.test_redirection_target(L!("file.txt"), RedirectionMode::input);
|
||||
let result = tester.test_redirection_target(L!("file.txt"), RedirectionMode::Input);
|
||||
assert!(result);
|
||||
|
||||
// Can't redirect from a missing file
|
||||
let result = tester.test_redirection_target(L!("notfile.txt"), RedirectionMode::input);
|
||||
let result = tester.test_redirection_target(L!("notfile.txt"), RedirectionMode::Input);
|
||||
assert!(!result);
|
||||
let result =
|
||||
tester.test_redirection_target(L!("bogus_path/file.txt"), RedirectionMode::input);
|
||||
tester.test_redirection_target(L!("bogus_path/file.txt"), RedirectionMode::Input);
|
||||
assert!(!result);
|
||||
|
||||
// Can't redirect from a directory.
|
||||
let result = tester.test_redirection_target(L!("somedir"), RedirectionMode::input);
|
||||
let result = tester.test_redirection_target(L!("somedir"), RedirectionMode::Input);
|
||||
assert!(!result);
|
||||
|
||||
// Can't redirect from an unreadable file.
|
||||
#[cfg(not(cygwin))] // Can't mark a file write-only on MSYS, this may work on true Cygwin
|
||||
{
|
||||
fs::set_permissions(&file_path, Permissions::from_mode(0o200)).unwrap();
|
||||
let result = tester.test_redirection_target(L!("file.txt"), RedirectionMode::input);
|
||||
let result = tester.test_redirection_target(L!("file.txt"), RedirectionMode::Input);
|
||||
assert!(!result);
|
||||
fs::set_permissions(&file_path, Permissions::from_mode(0o600)).unwrap();
|
||||
}
|
||||
|
||||
// try_input syntax highlighting reports an error even though the command will succeed.
|
||||
let result = tester.test_redirection_target(L!("file.txt"), RedirectionMode::try_input);
|
||||
let result = tester.test_redirection_target(L!("file.txt"), RedirectionMode::TryInput);
|
||||
assert!(result);
|
||||
let result = tester.test_redirection_target(L!("notfile.txt"), RedirectionMode::try_input);
|
||||
let result = tester.test_redirection_target(L!("notfile.txt"), RedirectionMode::TryInput);
|
||||
assert!(!result);
|
||||
let result =
|
||||
tester.test_redirection_target(L!("bogus_path/file.txt"), RedirectionMode::try_input);
|
||||
tester.test_redirection_target(L!("bogus_path/file.txt"), RedirectionMode::TryInput);
|
||||
assert!(!result);
|
||||
|
||||
// Test write redirections.
|
||||
// Overwrite an existing file.
|
||||
let result = tester.test_redirection_target(L!("file.txt"), RedirectionMode::overwrite);
|
||||
let result = tester.test_redirection_target(L!("file.txt"), RedirectionMode::Overwrite);
|
||||
assert!(result);
|
||||
|
||||
// Append to an existing file.
|
||||
let result = tester.test_redirection_target(L!("file.txt"), RedirectionMode::append);
|
||||
let result = tester.test_redirection_target(L!("file.txt"), RedirectionMode::Append);
|
||||
assert!(result);
|
||||
|
||||
// Write to a missing file.
|
||||
let result = tester.test_redirection_target(L!("newfile.txt"), RedirectionMode::overwrite);
|
||||
let result = tester.test_redirection_target(L!("newfile.txt"), RedirectionMode::Overwrite);
|
||||
assert!(result);
|
||||
|
||||
// No-clobber write to existing file should fail.
|
||||
let result = tester.test_redirection_target(L!("file.txt"), RedirectionMode::noclob);
|
||||
let result = tester.test_redirection_target(L!("file.txt"), RedirectionMode::NoClob);
|
||||
assert!(!result);
|
||||
|
||||
// No-clobber write to missing file should succeed.
|
||||
let result = tester.test_redirection_target(L!("unique.txt"), RedirectionMode::noclob);
|
||||
let result = tester.test_redirection_target(L!("unique.txt"), RedirectionMode::NoClob);
|
||||
assert!(result);
|
||||
|
||||
let write_modes = &[
|
||||
RedirectionMode::overwrite,
|
||||
RedirectionMode::append,
|
||||
RedirectionMode::noclob,
|
||||
RedirectionMode::Overwrite,
|
||||
RedirectionMode::Append,
|
||||
RedirectionMode::NoClob,
|
||||
];
|
||||
|
||||
// Can't write to a directory.
|
||||
@@ -640,28 +640,28 @@ fn test_redirections() {
|
||||
}
|
||||
|
||||
// Test fd redirections.
|
||||
assert!(tester.test_redirection_target(L!("-"), RedirectionMode::fd));
|
||||
assert!(tester.test_redirection_target(L!("0"), RedirectionMode::fd));
|
||||
assert!(tester.test_redirection_target(L!("1"), RedirectionMode::fd));
|
||||
assert!(tester.test_redirection_target(L!("2"), RedirectionMode::fd));
|
||||
assert!(tester.test_redirection_target(L!("3"), RedirectionMode::fd));
|
||||
assert!(tester.test_redirection_target(L!("500"), RedirectionMode::fd));
|
||||
assert!(tester.test_redirection_target(L!("-"), RedirectionMode::Fd));
|
||||
assert!(tester.test_redirection_target(L!("0"), RedirectionMode::Fd));
|
||||
assert!(tester.test_redirection_target(L!("1"), RedirectionMode::Fd));
|
||||
assert!(tester.test_redirection_target(L!("2"), RedirectionMode::Fd));
|
||||
assert!(tester.test_redirection_target(L!("3"), RedirectionMode::Fd));
|
||||
assert!(tester.test_redirection_target(L!("500"), RedirectionMode::Fd));
|
||||
|
||||
// We are base 10, despite the leading 0.
|
||||
assert!(tester.test_redirection_target(L!("000"), RedirectionMode::fd));
|
||||
assert!(tester.test_redirection_target(L!("01"), RedirectionMode::fd));
|
||||
assert!(tester.test_redirection_target(L!("07"), RedirectionMode::fd));
|
||||
assert!(tester.test_redirection_target(L!("000"), RedirectionMode::Fd));
|
||||
assert!(tester.test_redirection_target(L!("01"), RedirectionMode::Fd));
|
||||
assert!(tester.test_redirection_target(L!("07"), RedirectionMode::Fd));
|
||||
|
||||
// Invalid fd redirections.
|
||||
assert!(!tester.test_redirection_target(L!("0x2"), RedirectionMode::fd));
|
||||
assert!(!tester.test_redirection_target(L!("0x3F"), RedirectionMode::fd));
|
||||
assert!(!tester.test_redirection_target(L!("0F"), RedirectionMode::fd));
|
||||
assert!(!tester.test_redirection_target(L!("-1"), RedirectionMode::fd));
|
||||
assert!(!tester.test_redirection_target(L!("-0009"), RedirectionMode::fd));
|
||||
assert!(!tester.test_redirection_target(L!("--"), RedirectionMode::fd));
|
||||
assert!(!tester.test_redirection_target(L!("derp"), RedirectionMode::fd));
|
||||
assert!(!tester.test_redirection_target(L!("123boo"), RedirectionMode::fd));
|
||||
assert!(!tester.test_redirection_target(L!("18446744073709551616"), RedirectionMode::fd));
|
||||
assert!(!tester.test_redirection_target(L!("0x2"), RedirectionMode::Fd));
|
||||
assert!(!tester.test_redirection_target(L!("0x3F"), RedirectionMode::Fd));
|
||||
assert!(!tester.test_redirection_target(L!("0F"), RedirectionMode::Fd));
|
||||
assert!(!tester.test_redirection_target(L!("-1"), RedirectionMode::Fd));
|
||||
assert!(!tester.test_redirection_target(L!("-0009"), RedirectionMode::Fd));
|
||||
assert!(!tester.test_redirection_target(L!("--"), RedirectionMode::Fd));
|
||||
assert!(!tester.test_redirection_target(L!("derp"), RedirectionMode::Fd));
|
||||
assert!(!tester.test_redirection_target(L!("123boo"), RedirectionMode::Fd));
|
||||
assert!(!tester.test_redirection_target(L!("18446744073709551616"), RedirectionMode::Fd));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -223,14 +223,14 @@ fn command_is_valid(
|
||||
let mut implicit_cd_ok = true;
|
||||
if matches!(
|
||||
decoration,
|
||||
StatementDecoration::command | StatementDecoration::exec
|
||||
StatementDecoration::Command | StatementDecoration::Exec
|
||||
) {
|
||||
builtin_ok = false;
|
||||
function_ok = false;
|
||||
abbreviation_ok = false;
|
||||
command_ok = true;
|
||||
implicit_cd_ok = false;
|
||||
} else if decoration == StatementDecoration::builtin {
|
||||
} else if decoration == StatementDecoration::Builtin {
|
||||
builtin_ok = true;
|
||||
function_ok = false;
|
||||
abbreviation_ok = false;
|
||||
@@ -569,7 +569,7 @@ enum Mode {
|
||||
in_pos -= 1;
|
||||
}
|
||||
'?' => {
|
||||
if !feature_test(FeatureFlag::qmark_noglob) {
|
||||
if !feature_test(FeatureFlag::QuestionMarkNoGlob) {
|
||||
colors[in_pos] = HighlightSpec::with_fg(HighlightRole::operat);
|
||||
}
|
||||
}
|
||||
@@ -878,14 +878,14 @@ fn visit_keyword(&mut self, node: &dyn Keyword) {
|
||||
fn visit_token(&mut self, tok: &dyn Token) {
|
||||
let mut role = HighlightRole::normal;
|
||||
match tok.token_type() {
|
||||
ParseTokenType::end | ParseTokenType::pipe | ParseTokenType::background => {
|
||||
ParseTokenType::End | ParseTokenType::Pipe | ParseTokenType::Background => {
|
||||
role = HighlightRole::statement_terminator
|
||||
}
|
||||
ParseTokenType::left_brace | ParseTokenType::right_brace => {
|
||||
ParseTokenType::LeftBrace | ParseTokenType::RightBrace => {
|
||||
role = HighlightRole::keyword;
|
||||
}
|
||||
ParseTokenType::andand | ParseTokenType::oror => role = HighlightRole::operat,
|
||||
ParseTokenType::string => {
|
||||
ParseTokenType::AndAnd | ParseTokenType::OrOr => role = HighlightRole::operat,
|
||||
ParseTokenType::String => {
|
||||
// Assume all strings are params. This handles e.g. the variables a for header or
|
||||
// function header. Other strings (like arguments to commands) need more complex
|
||||
// handling, which occurs in their respective overrides of visit().
|
||||
@@ -1121,7 +1121,7 @@ fn visit(&mut self, node: &'a dyn Node) {
|
||||
return self.visit_keyword(keyword);
|
||||
}
|
||||
if let Some(token) = node.as_token() {
|
||||
if token.token_type() == ParseTokenType::end {
|
||||
if token.token_type() == ParseTokenType::End {
|
||||
self.visit_semi_nl(node);
|
||||
return;
|
||||
}
|
||||
@@ -1379,10 +1379,10 @@ macro_rules! validate {
|
||||
let mut param_valid_path = HighlightSpec::with_fg(HighlightRole::param);
|
||||
param_valid_path.valid_path = true;
|
||||
|
||||
let saved_flag = future_feature_flags::test(FeatureFlag::ampersand_nobg_in_token);
|
||||
future_feature_flags::set(FeatureFlag::ampersand_nobg_in_token, true);
|
||||
let saved_flag = future_feature_flags::test(FeatureFlag::AmpersandNoBgInToken);
|
||||
future_feature_flags::set(FeatureFlag::AmpersandNoBgInToken, true);
|
||||
let _restore_saved_flag = ScopeGuard::new((), |_| {
|
||||
future_feature_flags::set(FeatureFlag::ampersand_nobg_in_token, saved_flag);
|
||||
future_feature_flags::set(FeatureFlag::AmpersandNoBgInToken, saved_flag);
|
||||
});
|
||||
|
||||
let fg = HighlightSpec::with_fg;
|
||||
|
||||
@@ -311,7 +311,7 @@ pub fn append_history_item_to_buffer(item: &HistoryItem, buffer: &mut Vec<u8>) {
|
||||
/// Don't try mmap() on non-local filesystems.
|
||||
fn should_mmap() -> bool {
|
||||
// mmap only if we are known not-remote.
|
||||
path_get_data_remoteness() != DirRemoteness::remote
|
||||
path_get_data_remoteness() != DirRemoteness::Remote
|
||||
}
|
||||
|
||||
pub fn time_to_seconds(ts: SystemTime) -> i64 {
|
||||
|
||||
@@ -1284,7 +1284,7 @@ pub fn add_pending_with_file_detection(
|
||||
// Also skip it for 'echo'. This is because echo doesn't take file paths, but also
|
||||
// because the history file test wants to find the commands in the history file
|
||||
// immediately after running them, so it can't tolerate the asynchronous file detection.
|
||||
if stmt.decoration() == StatementDecoration::exec {
|
||||
if stmt.decoration() == StatementDecoration::Exec {
|
||||
needs_sync_write = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -906,7 +906,7 @@ fn readch(&mut self) -> CharEvent {
|
||||
|
||||
fn read_sequence_byte(&mut self, buffer: &mut Vec<u8>) -> Option<u8> {
|
||||
let fd = self.get_in_fd();
|
||||
let strict = feature_test(FeatureFlag::omit_term_workarounds);
|
||||
let strict = feature_test(FeatureFlag::OmitTermWorkarounds);
|
||||
let historical_millis = |ms| {
|
||||
if strict {
|
||||
LONG_READ_TIMEOUT
|
||||
|
||||
28
src/io.rs
28
src/io.rs
@@ -162,11 +162,11 @@ fn try_add_size(&mut self, delta: usize) -> bool {
|
||||
/// Describes what type of IO operation an io_data_t represents.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum IoMode {
|
||||
file,
|
||||
pipe,
|
||||
fd,
|
||||
close,
|
||||
bufferfill,
|
||||
File,
|
||||
Pipe,
|
||||
Fd,
|
||||
Close,
|
||||
BufferFill,
|
||||
}
|
||||
|
||||
/// Represents a FD redirection.
|
||||
@@ -196,7 +196,7 @@ pub fn new(fd: RawFd) -> Self {
|
||||
}
|
||||
impl IoData for IoClose {
|
||||
fn io_mode(&self) -> IoMode {
|
||||
IoMode::close
|
||||
IoMode::Close
|
||||
}
|
||||
fn fd(&self) -> RawFd {
|
||||
self.fd
|
||||
@@ -222,7 +222,7 @@ pub fn new(fd: RawFd, source_fd: RawFd) -> Self {
|
||||
}
|
||||
impl IoData for IoFd {
|
||||
fn io_mode(&self) -> IoMode {
|
||||
IoMode::fd
|
||||
IoMode::Fd
|
||||
}
|
||||
fn fd(&self) -> RawFd {
|
||||
self.fd
|
||||
@@ -251,7 +251,7 @@ pub fn new(fd: RawFd, file: File) -> Self {
|
||||
}
|
||||
impl IoData for IoFile {
|
||||
fn io_mode(&self) -> IoMode {
|
||||
IoMode::file
|
||||
IoMode::File
|
||||
}
|
||||
fn fd(&self) -> RawFd {
|
||||
self.fd
|
||||
@@ -283,7 +283,7 @@ pub fn new(fd: RawFd, is_input: bool, pipe_fd: OwnedFd) -> Self {
|
||||
}
|
||||
impl IoData for IoPipe {
|
||||
fn io_mode(&self) -> IoMode {
|
||||
IoMode::pipe
|
||||
IoMode::Pipe
|
||||
}
|
||||
fn fd(&self) -> RawFd {
|
||||
self.fd
|
||||
@@ -367,7 +367,7 @@ pub fn finish(filler: Arc<IoBufferfill>) -> SeparatedBuffer {
|
||||
}
|
||||
impl IoData for IoBufferfill {
|
||||
fn io_mode(&self) -> IoMode {
|
||||
IoMode::bufferfill
|
||||
IoMode::BufferFill
|
||||
}
|
||||
fn fd(&self) -> RawFd {
|
||||
self.target
|
||||
@@ -555,7 +555,7 @@ pub fn append_from_specs(&mut self, specs: &RedirectionSpecList, pwd: &wstr) ->
|
||||
|
||||
for spec in specs {
|
||||
match spec.mode {
|
||||
RedirectionMode::fd => {
|
||||
RedirectionMode::Fd => {
|
||||
if spec.is_close() {
|
||||
self.push(Arc::new(IoClose::new(spec.fd)));
|
||||
} else {
|
||||
@@ -578,7 +578,7 @@ pub fn append_from_specs(&mut self, specs: &RedirectionSpecList, pwd: &wstr) ->
|
||||
Err(err) => {
|
||||
if oflags.contains(OFlag::O_EXCL) && err == nix::Error::EEXIST {
|
||||
FLOGF!(warning, NOCLOB_ERROR, spec.target);
|
||||
} else if spec.mode != RedirectionMode::try_input
|
||||
} else if spec.mode != RedirectionMode::TryInput
|
||||
&& should_flog!(warning)
|
||||
{
|
||||
print_error(errno::errno().0, &spec.target);
|
||||
@@ -586,7 +586,7 @@ pub fn append_from_specs(&mut self, specs: &RedirectionSpecList, pwd: &wstr) ->
|
||||
// If opening a file fails, insert a closed FD instead of the file redirection
|
||||
// and return false. This lets execution potentially recover and at least gives
|
||||
// the shell a chance to gracefully regain control of the shell (see #7038).
|
||||
if spec.mode != RedirectionMode::try_input {
|
||||
if spec.mode != RedirectionMode::TryInput {
|
||||
self.push(Arc::new(IoClose::new(spec.fd)));
|
||||
have_error = true;
|
||||
continue;
|
||||
@@ -762,7 +762,7 @@ pub fn new(fd: RawFd) -> Self {
|
||||
assert!(fd >= 0, "Invalid fd");
|
||||
FdOutputStream {
|
||||
fd,
|
||||
sigcheck: SigChecker::new(Topic::sighupint),
|
||||
sigcheck: SigChecker::new(Topic::SigHupInt),
|
||||
errored: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,7 +421,7 @@ fn ctrl_to_symbol(buf: &mut WString, c: char) {
|
||||
fn must_escape(is_first_in_token: bool, c: char) -> bool {
|
||||
"[]()<>{}*\\$;&|'\"".contains(c)
|
||||
|| (is_first_in_token && "~#".contains(c))
|
||||
|| (c == '?' && !feature_test(FeatureFlag::qmark_noglob))
|
||||
|| (c == '?' && !feature_test(FeatureFlag::QuestionMarkNoGlob))
|
||||
}
|
||||
|
||||
fn ascii_printable_to_symbol(buf: &mut WString, is_first_in_token: bool, c: char) {
|
||||
|
||||
@@ -53,24 +53,24 @@ pub fn as_usize(self) -> std::ops::Range<usize> {
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub enum ParseTokenType {
|
||||
#[default]
|
||||
invalid = 1,
|
||||
Invalid = 1,
|
||||
|
||||
// Terminal types.
|
||||
string,
|
||||
pipe,
|
||||
left_brace,
|
||||
right_brace,
|
||||
redirection,
|
||||
background,
|
||||
andand,
|
||||
oror,
|
||||
end,
|
||||
String,
|
||||
Pipe,
|
||||
LeftBrace,
|
||||
RightBrace,
|
||||
Redirection,
|
||||
Background,
|
||||
AndAnd,
|
||||
OrOr,
|
||||
End,
|
||||
// Special terminal type that means no more tokens forthcoming.
|
||||
terminate,
|
||||
Terminate,
|
||||
// Very special terminal types that don't appear in the production list.
|
||||
error,
|
||||
tokenizer_error,
|
||||
comment,
|
||||
Error,
|
||||
TokenizerError,
|
||||
Comment,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
@@ -102,45 +102,45 @@ pub enum ParseKeyword {
|
||||
// Statement decorations like 'command' or 'exec'.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum StatementDecoration {
|
||||
none,
|
||||
command,
|
||||
builtin,
|
||||
exec,
|
||||
None,
|
||||
Command,
|
||||
Builtin,
|
||||
Exec,
|
||||
}
|
||||
|
||||
// Parse error code list.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub enum ParseErrorCode {
|
||||
#[default]
|
||||
none,
|
||||
None,
|
||||
|
||||
// Matching values from enum parser_error.
|
||||
syntax,
|
||||
cmdsubst,
|
||||
Syntax,
|
||||
CmdSubst,
|
||||
|
||||
generic, // unclassified error types
|
||||
Generic, // unclassified error types
|
||||
|
||||
// Tokenizer errors.
|
||||
tokenizer_unterminated_quote,
|
||||
tokenizer_unterminated_subshell,
|
||||
tokenizer_unterminated_slice,
|
||||
tokenizer_unterminated_escape,
|
||||
tokenizer_other,
|
||||
TokenizerUnterminatedQuote,
|
||||
TokenizerUnterminatedSubshell,
|
||||
TokenizerUnterminatedSlice,
|
||||
TokenizerUnterminatedEscape,
|
||||
TokenizerOther,
|
||||
|
||||
unbalancing_end, // end outside of block
|
||||
unbalancing_else, // else outside of if
|
||||
unbalancing_case, // case outside of switch
|
||||
unbalancing_brace, // } outside of {
|
||||
bare_variable_assignment, // a=b without command
|
||||
andor_in_pipeline, // "and" or "or" after a pipe
|
||||
UnbalancingEnd, // end outside of block
|
||||
UnbalancingElse, // else outside of if
|
||||
UnbalancingCase, // case outside of switch
|
||||
UnbalancingBrace, // } outside of {
|
||||
BareVariableAssignment, // a=b without command
|
||||
AndOrInPipeline, // "and" or "or" after a pipe
|
||||
}
|
||||
|
||||
// The location of a pipeline.
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum PipelinePosition {
|
||||
none, // not part of a pipeline
|
||||
first, // first command in a pipeline
|
||||
subsequent, // second or further command in a pipeline
|
||||
None, // not part of a pipeline
|
||||
First, // first command in a pipeline
|
||||
Subsequent, // second or further command in a pipeline
|
||||
}
|
||||
|
||||
impl SourceRange {
|
||||
@@ -191,20 +191,20 @@ impl ParseTokenType {
|
||||
/// Return a string describing the token type.
|
||||
pub fn to_wstr(self) -> &'static wstr {
|
||||
match self {
|
||||
ParseTokenType::comment => L!("ParseTokenType::comment"),
|
||||
ParseTokenType::error => L!("ParseTokenType::error"),
|
||||
ParseTokenType::tokenizer_error => L!("ParseTokenType::tokenizer_error"),
|
||||
ParseTokenType::background => L!("ParseTokenType::background"),
|
||||
ParseTokenType::end => L!("ParseTokenType::end"),
|
||||
ParseTokenType::pipe => L!("ParseTokenType::pipe"),
|
||||
ParseTokenType::left_brace => L!("ParseTokenType::lbrace"),
|
||||
ParseTokenType::right_brace => L!("ParseTokenType::rbrace"),
|
||||
ParseTokenType::redirection => L!("ParseTokenType::redirection"),
|
||||
ParseTokenType::string => L!("ParseTokenType::string"),
|
||||
ParseTokenType::andand => L!("ParseTokenType::andand"),
|
||||
ParseTokenType::oror => L!("ParseTokenType::oror"),
|
||||
ParseTokenType::terminate => L!("ParseTokenType::terminate"),
|
||||
ParseTokenType::invalid => L!("ParseTokenType::invalid"),
|
||||
ParseTokenType::Comment => L!("ParseTokenType::comment"),
|
||||
ParseTokenType::Error => L!("ParseTokenType::error"),
|
||||
ParseTokenType::TokenizerError => L!("ParseTokenType::tokenizer_error"),
|
||||
ParseTokenType::Background => L!("ParseTokenType::background"),
|
||||
ParseTokenType::End => L!("ParseTokenType::end"),
|
||||
ParseTokenType::Pipe => L!("ParseTokenType::pipe"),
|
||||
ParseTokenType::LeftBrace => L!("ParseTokenType::lbrace"),
|
||||
ParseTokenType::RightBrace => L!("ParseTokenType::rbrace"),
|
||||
ParseTokenType::Redirection => L!("ParseTokenType::redirection"),
|
||||
ParseTokenType::String => L!("ParseTokenType::string"),
|
||||
ParseTokenType::AndAnd => L!("ParseTokenType::andand"),
|
||||
ParseTokenType::OrOr => L!("ParseTokenType::oror"),
|
||||
ParseTokenType::Terminate => L!("ParseTokenType::terminate"),
|
||||
ParseTokenType::Invalid => L!("ParseTokenType::invalid"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -404,19 +404,19 @@ pub fn token_type_user_presentable_description(
|
||||
return sprintf!("keyword: '%s'", keyword.to_wstr());
|
||||
}
|
||||
match type_ {
|
||||
ParseTokenType::string => L!("a string").to_owned(),
|
||||
ParseTokenType::pipe => L!("a pipe").to_owned(),
|
||||
ParseTokenType::redirection => L!("a redirection").to_owned(),
|
||||
ParseTokenType::background => L!("a '&'").to_owned(),
|
||||
ParseTokenType::left_brace => L!("a '{'").to_owned(),
|
||||
ParseTokenType::right_brace => L!("a '}'").to_owned(),
|
||||
ParseTokenType::andand => L!("'&&'").to_owned(),
|
||||
ParseTokenType::oror => L!("'||'").to_owned(),
|
||||
ParseTokenType::end => L!("end of the statement").to_owned(),
|
||||
ParseTokenType::terminate => L!("end of the input").to_owned(),
|
||||
ParseTokenType::error => L!("a parse error").to_owned(),
|
||||
ParseTokenType::tokenizer_error => L!("an incomplete token").to_owned(),
|
||||
ParseTokenType::comment => L!("a comment").to_owned(),
|
||||
ParseTokenType::String => L!("a string").to_owned(),
|
||||
ParseTokenType::Pipe => L!("a pipe").to_owned(),
|
||||
ParseTokenType::Redirection => L!("a redirection").to_owned(),
|
||||
ParseTokenType::Background => L!("a '&'").to_owned(),
|
||||
ParseTokenType::LeftBrace => L!("a '{'").to_owned(),
|
||||
ParseTokenType::RightBrace => L!("a '}'").to_owned(),
|
||||
ParseTokenType::AndAnd => L!("'&&'").to_owned(),
|
||||
ParseTokenType::OrOr => L!("'||'").to_owned(),
|
||||
ParseTokenType::End => L!("end of the statement").to_owned(),
|
||||
ParseTokenType::Terminate => L!("end of the input").to_owned(),
|
||||
ParseTokenType::Error => L!("a parse error").to_owned(),
|
||||
ParseTokenType::TokenizerError => L!("an incomplete token").to_owned(),
|
||||
ParseTokenType::Comment => L!("a comment").to_owned(),
|
||||
_ => sprintf!("a %s", type_.to_wstr()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,16 +62,16 @@
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub enum EndExecutionReason {
|
||||
/// Evaluation was successful.
|
||||
ok,
|
||||
Ok,
|
||||
|
||||
/// Evaluation was skipped due to control flow (break or return).
|
||||
control_flow,
|
||||
ControlFlow,
|
||||
|
||||
/// Evaluation was cancelled, e.g. because of a signal or exit.
|
||||
cancelled,
|
||||
Cancelled,
|
||||
|
||||
/// A parse error or failed expansion (but not an error exit status from a command).
|
||||
error,
|
||||
Error,
|
||||
}
|
||||
|
||||
pub struct ExecutionContext<'a> {
|
||||
@@ -108,7 +108,7 @@ macro_rules! report_error_formatted {
|
||||
let mut error = ParseError::default();
|
||||
error.source_start = r.start();
|
||||
error.source_length = r.length();
|
||||
error.code = ParseErrorCode::syntax; // hackish
|
||||
error.code = ParseErrorCode::Syntax; // hackish
|
||||
error.text = $text;
|
||||
$self.report_errors($ctx, $status, &vec![error])
|
||||
}};
|
||||
@@ -224,18 +224,18 @@ fn check_end_execution(&self, ctx: &OperationContext<'_>) -> Option<EndExecution
|
||||
// If one of our jobs ended with SIGINT, we stop execution.
|
||||
// Likewise if fish itself got a SIGINT, or if something ran exit, etc.
|
||||
if self.cancel_signal.is_some() || ctx.check_cancel() || fish_is_unwinding_for_exit() {
|
||||
return Some(EndExecutionReason::cancelled);
|
||||
return Some(EndExecutionReason::Cancelled);
|
||||
}
|
||||
let parser = ctx.parser();
|
||||
let ld = &parser.libdata();
|
||||
if ld.exit_current_script {
|
||||
return Some(EndExecutionReason::cancelled);
|
||||
return Some(EndExecutionReason::Cancelled);
|
||||
}
|
||||
if ld.returning {
|
||||
return Some(EndExecutionReason::control_flow);
|
||||
return Some(EndExecutionReason::ControlFlow);
|
||||
}
|
||||
if ld.loop_status != LoopStatus::normals {
|
||||
return Some(EndExecutionReason::control_flow);
|
||||
return Some(EndExecutionReason::ControlFlow);
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -262,7 +262,7 @@ fn report_errors(
|
||||
// Mark status.
|
||||
ctx.parser().set_last_statuses(Statuses::just(status));
|
||||
}
|
||||
EndExecutionReason::error
|
||||
EndExecutionReason::Error
|
||||
}
|
||||
|
||||
/// Command not found support.
|
||||
@@ -322,7 +322,7 @@ fn handle_command_not_found(
|
||||
let args = Self::get_argument_nodes_no_redirs(&statement.args_or_redirs);
|
||||
let arg_result =
|
||||
self.expand_arguments_from_nodes(ctx, &args, &mut event_args, Globspec::failglob);
|
||||
if arg_result != EndExecutionReason::ok {
|
||||
if arg_result != EndExecutionReason::Ok {
|
||||
return arg_result;
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ fn handle_command_not_found(
|
||||
let mut list = RedirectionSpecList::new();
|
||||
list.push(RedirectionSpec::new(
|
||||
STDOUT_FILENO,
|
||||
RedirectionMode::fd,
|
||||
RedirectionMode::Fd,
|
||||
L!("2").to_owned(),
|
||||
));
|
||||
io.append_from_specs(&list, L!(""));
|
||||
@@ -420,7 +420,7 @@ fn infinite_recursive_statement_in_job_list<'b>(
|
||||
|
||||
// Ignore statements with decorations like 'builtin' or 'command', since those
|
||||
// are not infinite recursion. In particular that is what enables 'wrapper functions'.
|
||||
if dc.decoration() != StatementDecoration::none {
|
||||
if dc.decoration() != StatementDecoration::None {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -514,7 +514,7 @@ fn expand_command(
|
||||
return self.report_wildcard_error(ctx, statement);
|
||||
}
|
||||
ExpandResultCode::cancel => {
|
||||
return EndExecutionReason::cancelled;
|
||||
return EndExecutionReason::Cancelled;
|
||||
}
|
||||
ExpandResultCode::ok => {}
|
||||
}
|
||||
@@ -536,7 +536,7 @@ fn expand_command(
|
||||
//
|
||||
// (skipping in no-exec because we don't have the actual variable value)
|
||||
if !no_exec()
|
||||
&& &unescape_keyword(TokenType::string, unexp_cmd) != out_cmd
|
||||
&& &unescape_keyword(TokenType::String, unexp_cmd) != out_cmd
|
||||
&& parser_keywords_is_subcommand(out_cmd)
|
||||
{
|
||||
return report_error!(
|
||||
@@ -547,7 +547,7 @@ fn expand_command(
|
||||
"The expanded command is a keyword."
|
||||
);
|
||||
}
|
||||
EndExecutionReason::ok
|
||||
EndExecutionReason::Ok
|
||||
}
|
||||
|
||||
/// Indicates whether a job is a simple block (one block, no redirections).
|
||||
@@ -583,10 +583,10 @@ fn process_type_for_command(
|
||||
// Determine the process type, which depends on the statement decoration (command, builtin,
|
||||
// etc).
|
||||
match statement.decoration() {
|
||||
StatementDecoration::exec => ProcessType::Exec,
|
||||
StatementDecoration::command => ProcessType::External,
|
||||
StatementDecoration::builtin => ProcessType::Builtin,
|
||||
StatementDecoration::none => {
|
||||
StatementDecoration::Exec => ProcessType::Exec,
|
||||
StatementDecoration::Command => ProcessType::External,
|
||||
StatementDecoration::Builtin => ProcessType::Builtin,
|
||||
StatementDecoration::None => {
|
||||
if function::exists(cmd, ctx.parser()) {
|
||||
ProcessType::Function
|
||||
} else if builtin_exists(cmd) {
|
||||
@@ -606,7 +606,7 @@ fn apply_variable_assignments(
|
||||
block: &mut Option<BlockId>,
|
||||
) -> EndExecutionReason {
|
||||
if variable_assignment_list.is_empty() {
|
||||
return EndExecutionReason::ok;
|
||||
return EndExecutionReason::Ok;
|
||||
}
|
||||
*block = Some(ctx.parser().push_block(Block::variable_assignment_block()));
|
||||
for variable_assignment in variable_assignment_list {
|
||||
@@ -633,7 +633,7 @@ fn apply_variable_assignments(
|
||||
return self.report_errors(ctx, expand_ret.status, &errors);
|
||||
}
|
||||
ExpandResultCode::cancel => {
|
||||
return EndExecutionReason::cancelled;
|
||||
return EndExecutionReason::Cancelled;
|
||||
}
|
||||
ExpandResultCode::wildcard_no_match // nullglob (equivalent to set)
|
||||
| ExpandResultCode::ok => {}
|
||||
@@ -651,7 +651,7 @@ fn apply_variable_assignments(
|
||||
ctx.parser()
|
||||
.set_var_and_fire(variable_name, EnvMode::LOCAL | EnvMode::EXPORT, vals);
|
||||
}
|
||||
EndExecutionReason::ok
|
||||
EndExecutionReason::Ok
|
||||
}
|
||||
|
||||
// These create process_t structures from statements.
|
||||
@@ -671,7 +671,7 @@ fn populate_job_process(
|
||||
ctx.parser().pop_block(block);
|
||||
}
|
||||
});
|
||||
if result != EndExecutionReason::ok {
|
||||
if result != EndExecutionReason::Ok {
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -722,13 +722,13 @@ fn populate_plain_process(
|
||||
let mut cmd = WString::new();
|
||||
let mut args_from_cmd_expansion = vec![];
|
||||
let ret = self.expand_command(ctx, statement, &mut cmd, &mut args_from_cmd_expansion);
|
||||
if ret != EndExecutionReason::ok {
|
||||
if ret != EndExecutionReason::Ok {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// For no-exec, having an empty command is okay. We can't do anything more with it tho.
|
||||
if no_exec() {
|
||||
return EndExecutionReason::ok;
|
||||
return EndExecutionReason::Ok;
|
||||
}
|
||||
|
||||
assert!(
|
||||
@@ -749,7 +749,7 @@ fn populate_plain_process(
|
||||
path = external_cmd.path;
|
||||
} else {
|
||||
// If the specified command does not exist, and is undecorated, try using an implicit cd.
|
||||
if statement.decoration() == StatementDecoration::none {
|
||||
if statement.decoration() == StatementDecoration::None {
|
||||
// Implicit cd requires an empty argument and redirection list.
|
||||
if statement.args_or_redirs.is_empty() {
|
||||
// Ok, no arguments or redirections; check to see if the command is a directory.
|
||||
@@ -808,14 +808,14 @@ fn populate_plain_process(
|
||||
let arg_nodes = Self::get_argument_nodes_no_redirs(&statement.args_or_redirs);
|
||||
let arg_result =
|
||||
self.expand_arguments_from_nodes(ctx, &arg_nodes, &mut cmd_args, glob_behavior);
|
||||
if arg_result != EndExecutionReason::ok {
|
||||
if arg_result != EndExecutionReason::Ok {
|
||||
return arg_result;
|
||||
}
|
||||
|
||||
// The set of IO redirections that we construct for the process.
|
||||
let reason =
|
||||
self.determine_redirections(ctx, &statement.args_or_redirs, &mut redirections);
|
||||
if reason != EndExecutionReason::ok {
|
||||
if reason != EndExecutionReason::Ok {
|
||||
return reason;
|
||||
}
|
||||
}
|
||||
@@ -825,7 +825,7 @@ fn populate_plain_process(
|
||||
proc.set_argv(cmd_args);
|
||||
proc.set_redirection_specs(redirections);
|
||||
proc.actual_cmd = external_cmd;
|
||||
EndExecutionReason::ok
|
||||
EndExecutionReason::Ok
|
||||
}
|
||||
|
||||
fn populate_block_process(
|
||||
@@ -848,7 +848,7 @@ fn populate_block_process(
|
||||
|
||||
let mut redirections = RedirectionSpecList::new();
|
||||
let reason = self.determine_redirections(ctx, args_or_redirs, &mut redirections);
|
||||
if reason == EndExecutionReason::ok {
|
||||
if reason == EndExecutionReason::Ok {
|
||||
proc.typ = ProcessType::BlockNode(NodeRef::new(Arc::clone(self.pstree()), statement));
|
||||
proc.set_redirection_specs(redirections);
|
||||
}
|
||||
@@ -910,7 +910,7 @@ fn run_for_statement(
|
||||
let arg_nodes = Self::get_argument_nodes(&header.args);
|
||||
let ret =
|
||||
self.expand_arguments_from_nodes(ctx, &arg_nodes, &mut arguments, Globspec::nullglob);
|
||||
if ret != EndExecutionReason::ok {
|
||||
if ret != EndExecutionReason::Ok {
|
||||
return ret;
|
||||
}
|
||||
let var = ctx.parser().vars().get(&for_var_name);
|
||||
@@ -939,7 +939,7 @@ fn run_for_statement(
|
||||
let evt = Event::variable_set(for_var_name.clone());
|
||||
|
||||
// Now drive the for loop.
|
||||
let mut ret = EndExecutionReason::ok;
|
||||
let mut ret = EndExecutionReason::Ok;
|
||||
for val in arguments {
|
||||
if let Some(reason) = self.check_end_execution(ctx) {
|
||||
ret = reason;
|
||||
@@ -963,7 +963,7 @@ fn run_for_statement(
|
||||
self.run_job_list(ctx, block_contents, Some(fb));
|
||||
ctx.parser().pop_block(fb);
|
||||
|
||||
if self.check_end_execution(ctx) == Some(EndExecutionReason::control_flow) {
|
||||
if self.check_end_execution(ctx) == Some(EndExecutionReason::ControlFlow) {
|
||||
// Handle break or continue.
|
||||
let do_break = ctx.parser().libdata().loop_status == LoopStatus::breaks;
|
||||
ctx.parser().libdata_mut().loop_status = LoopStatus::normals;
|
||||
@@ -983,7 +983,7 @@ fn run_if_statement(
|
||||
statement: &'a ast::IfStatement,
|
||||
associated_block: Option<BlockId>,
|
||||
) -> EndExecutionReason {
|
||||
let mut result = EndExecutionReason::ok;
|
||||
let mut result = EndExecutionReason::Ok;
|
||||
|
||||
// We have a sequence of if clauses, with a final else, resulting in a single job list that we
|
||||
// execute.
|
||||
@@ -1008,10 +1008,10 @@ fn run_if_statement(
|
||||
// in accordance with historic behavior.
|
||||
let mut cond_ret =
|
||||
self.run_job_conjunction(ctx, &if_clause.condition, associated_block);
|
||||
if cond_ret == EndExecutionReason::ok {
|
||||
if cond_ret == EndExecutionReason::Ok {
|
||||
cond_ret = self.run_andor_job_list(ctx, &if_clause.andor_tail, associated_block);
|
||||
}
|
||||
let take_branch = cond_ret == EndExecutionReason::ok
|
||||
let take_branch = cond_ret == EndExecutionReason::Ok
|
||||
&& ctx.parser().get_last_status() == EXIT_SUCCESS;
|
||||
|
||||
if take_branch {
|
||||
@@ -1092,7 +1092,7 @@ fn run_switch_statement(
|
||||
return self.report_errors(ctx, expand_ret.status, &errors);
|
||||
}
|
||||
ExpandResultCode::cancel => {
|
||||
return EndExecutionReason::cancelled;
|
||||
return EndExecutionReason::Cancelled;
|
||||
}
|
||||
ExpandResultCode::wildcard_no_match => {
|
||||
return self.report_wildcard_error(ctx, &statement.argument);
|
||||
@@ -1122,7 +1122,7 @@ fn run_switch_statement(
|
||||
switch_values_expanded.remove(0).completion
|
||||
};
|
||||
|
||||
let mut result = EndExecutionReason::ok;
|
||||
let mut result = EndExecutionReason::Ok;
|
||||
|
||||
trace_if_enabled_with_args(ctx.parser(), L!("switch"), &[&switch_value_expanded]);
|
||||
let sb = ctx.parser().push_block(Block::switch_block());
|
||||
@@ -1146,7 +1146,7 @@ fn run_switch_statement(
|
||||
&mut case_args,
|
||||
Globspec::failglob,
|
||||
);
|
||||
if case_result == EndExecutionReason::ok {
|
||||
if case_result == EndExecutionReason::Ok {
|
||||
for arg in case_args {
|
||||
// Unescape wildcards so they can be expanded again.
|
||||
let unescaped_arg = parse_util_unescape_wildcards(&arg);
|
||||
@@ -1164,7 +1164,7 @@ fn run_switch_statement(
|
||||
|
||||
if let Some(case_item) = matching_case_item {
|
||||
// Success, evaluate the job list.
|
||||
assert!(result == EndExecutionReason::ok, "Expected success");
|
||||
assert!(result == EndExecutionReason::Ok, "Expected success");
|
||||
result = self.run_job_list(ctx, &case_item.body, Some(sb));
|
||||
}
|
||||
|
||||
@@ -1180,7 +1180,7 @@ fn run_while_statement(
|
||||
contents: &'a ast::JobList,
|
||||
associated_block: Option<BlockId>,
|
||||
) -> EndExecutionReason {
|
||||
let mut ret = EndExecutionReason::ok;
|
||||
let mut ret = EndExecutionReason::Ok;
|
||||
|
||||
// "The exit status of the while loop shall be the exit status of the last compound-list-2
|
||||
// executed, or zero if none was executed."
|
||||
@@ -1208,14 +1208,14 @@ fn run_while_statement(
|
||||
|
||||
// Check the condition.
|
||||
let mut cond_ret = self.run_job_conjunction(ctx, &header.condition, associated_block);
|
||||
if cond_ret == EndExecutionReason::ok {
|
||||
if cond_ret == EndExecutionReason::Ok {
|
||||
cond_ret = self.run_andor_job_list(ctx, &header.andor_tail, associated_block);
|
||||
}
|
||||
|
||||
// If the loop condition failed to execute, then exit the loop without modifying the exit
|
||||
// status. If the loop condition executed with a failure status, restore the status and then
|
||||
// exit the loop.
|
||||
if cond_ret != EndExecutionReason::ok {
|
||||
if cond_ret != EndExecutionReason::Ok {
|
||||
break;
|
||||
} else if ctx.parser().get_last_status() != EXIT_SUCCESS {
|
||||
ctx.parser().set_last_statuses(cond_saved_status);
|
||||
@@ -1236,7 +1236,7 @@ fn run_while_statement(
|
||||
let cancel_reason = self.check_end_execution(ctx);
|
||||
ctx.parser().pop_block(wb);
|
||||
|
||||
if cancel_reason == Some(EndExecutionReason::control_flow) {
|
||||
if cancel_reason == Some(EndExecutionReason::ControlFlow) {
|
||||
// Handle break or continue.
|
||||
let do_break = ctx.parser().libdata().loop_status == LoopStatus::breaks;
|
||||
ctx.parser().libdata_mut().loop_status = LoopStatus::normals;
|
||||
@@ -1271,7 +1271,7 @@ fn run_function_statement(
|
||||
let result =
|
||||
self.expand_arguments_from_nodes(ctx, &arg_nodes, &mut arguments, Globspec::failglob);
|
||||
|
||||
if result != EndExecutionReason::ok {
|
||||
if result != EndExecutionReason::Ok {
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1367,13 +1367,13 @@ fn expand_arguments_from_nodes(
|
||||
return self.report_errors(ctx, expand_ret.status, &errors);
|
||||
}
|
||||
ExpandResultCode::cancel => {
|
||||
return EndExecutionReason::cancelled;
|
||||
return EndExecutionReason::Cancelled;
|
||||
}
|
||||
ExpandResultCode::wildcard_no_match => {
|
||||
if glob_behavior == Globspec::failglob {
|
||||
// For no_exec, ignore the error - this might work at runtime.
|
||||
if no_exec() {
|
||||
return EndExecutionReason::ok;
|
||||
return EndExecutionReason::Ok;
|
||||
}
|
||||
// Report the unmatched wildcard error and stop processing.
|
||||
return self.report_wildcard_error(ctx, arg_node);
|
||||
@@ -1398,7 +1398,7 @@ fn expand_arguments_from_nodes(
|
||||
return ret;
|
||||
}
|
||||
|
||||
EndExecutionReason::ok
|
||||
EndExecutionReason::Ok
|
||||
}
|
||||
|
||||
// Determines the list of redirections for a node.
|
||||
@@ -1453,7 +1453,7 @@ fn determine_redirections(
|
||||
"Invalid redirection target: %s",
|
||||
target
|
||||
);
|
||||
if oper.mode == RedirectionMode::input && {
|
||||
if oper.mode == RedirectionMode::Input && {
|
||||
let redir_unexpanded = self.node_source(redir_node);
|
||||
redir_unexpanded.starts_with(L!("<("))
|
||||
&& match parse_util_locate_cmdsubst_range(
|
||||
@@ -1480,7 +1480,7 @@ fn determine_redirections(
|
||||
let spec = RedirectionSpec::new(oper.fd, oper.mode, target);
|
||||
|
||||
// Validate this spec.
|
||||
if spec.mode == RedirectionMode::fd
|
||||
if spec.mode == RedirectionMode::Fd
|
||||
&& !spec.is_close()
|
||||
&& spec.get_target_as_fd().is_none()
|
||||
{
|
||||
@@ -1501,7 +1501,7 @@ fn determine_redirections(
|
||||
out_redirections.push(get_stderr_merge());
|
||||
}
|
||||
}
|
||||
EndExecutionReason::ok
|
||||
EndExecutionReason::Ok
|
||||
}
|
||||
|
||||
fn run_1_job(
|
||||
@@ -1516,7 +1516,7 @@ fn run_1_job(
|
||||
|
||||
// We definitely do not want to execute anything if we're told we're --no-execute!
|
||||
if no_exec() {
|
||||
return EndExecutionReason::ok;
|
||||
return EndExecutionReason::Ok;
|
||||
}
|
||||
|
||||
// Increment the eval_level for the duration of this command.
|
||||
@@ -1567,7 +1567,7 @@ fn run_1_job(
|
||||
|
||||
let statement = &job_node.statement;
|
||||
assert!(statement_is_redirectable_block(statement));
|
||||
if result == EndExecutionReason::ok {
|
||||
if result == EndExecutionReason::Ok {
|
||||
result = match statement {
|
||||
Statement::Block(block_statement) => {
|
||||
self.run_block_statement(ctx, block_statement, associated_block)
|
||||
@@ -1626,7 +1626,7 @@ fn run_1_job(
|
||||
ScopeGuarding::commit(_caller_id);
|
||||
|
||||
// Clean up the job on failure or cancellation.
|
||||
if pop_result == EndExecutionReason::ok {
|
||||
if pop_result == EndExecutionReason::Ok {
|
||||
self.setup_group(ctx, &mut job);
|
||||
assert!(job.group.is_some(), "Should have a group");
|
||||
}
|
||||
@@ -1634,7 +1634,7 @@ fn run_1_job(
|
||||
// Now that we're done mutating the Job, we can stick it in an Arc
|
||||
let job = Rc::new(job);
|
||||
|
||||
if pop_result == EndExecutionReason::ok {
|
||||
if pop_result == EndExecutionReason::Ok {
|
||||
// Give the job to the parser - it will clean it up.
|
||||
{
|
||||
let parser = ctx.parser();
|
||||
@@ -1669,7 +1669,7 @@ fn run_1_job(
|
||||
profile_item.duration = ProfileItem::now() - start_time;
|
||||
profile_item.level = ctx.parser().scope().eval_level;
|
||||
profile_item.cmd = job.command().to_owned();
|
||||
profile_item.skipped = pop_result != EndExecutionReason::ok;
|
||||
profile_item.skipped = pop_result != EndExecutionReason::Ok;
|
||||
}
|
||||
|
||||
job_reap(ctx.parser(), false); // clean up jobs
|
||||
@@ -1705,7 +1705,7 @@ fn test_and_run_1_job_conjunction(
|
||||
}
|
||||
// Skipping is treated as success.
|
||||
if skip {
|
||||
EndExecutionReason::ok
|
||||
EndExecutionReason::Ok
|
||||
} else {
|
||||
self.run_job_conjunction(ctx, jc, associated_block)
|
||||
}
|
||||
@@ -1722,7 +1722,7 @@ fn run_job_conjunction(
|
||||
}
|
||||
let mut result = self.run_1_job(ctx, &job_expr.job, associated_block);
|
||||
for jc in &job_expr.continuations {
|
||||
if result != EndExecutionReason::ok {
|
||||
if result != EndExecutionReason::Ok {
|
||||
return result;
|
||||
}
|
||||
if let Some(reason) = self.check_end_execution(ctx) {
|
||||
@@ -1731,11 +1731,11 @@ fn run_job_conjunction(
|
||||
// Check the conjunction type.
|
||||
let last_status = ctx.parser().get_last_status();
|
||||
let skip = match jc.conjunction.token_type() {
|
||||
ParseTokenType::andand => {
|
||||
ParseTokenType::AndAnd => {
|
||||
// AND. Skip if the last job failed.
|
||||
last_status != 0
|
||||
}
|
||||
ParseTokenType::oror => {
|
||||
ParseTokenType::OrOr => {
|
||||
// OR. Skip if the last job succeeded.
|
||||
last_status == 0
|
||||
}
|
||||
@@ -1754,7 +1754,7 @@ fn run_job_list(
|
||||
job_list_node: &'a ast::JobList,
|
||||
associated_block: Option<BlockId>,
|
||||
) -> EndExecutionReason {
|
||||
let mut result = EndExecutionReason::ok;
|
||||
let mut result = EndExecutionReason::Ok;
|
||||
for jc in job_list_node {
|
||||
result = self.test_and_run_1_job_conjunction(ctx, jc, associated_block);
|
||||
}
|
||||
@@ -1768,7 +1768,7 @@ fn run_andor_job_list(
|
||||
job_list_node: &'a ast::AndorJobList,
|
||||
associated_block: Option<BlockId>,
|
||||
) -> EndExecutionReason {
|
||||
let mut result = EndExecutionReason::ok;
|
||||
let mut result = EndExecutionReason::Ok;
|
||||
for aoj in job_list_node {
|
||||
result = self.test_and_run_1_job_conjunction(ctx, &aoj.job, associated_block);
|
||||
}
|
||||
@@ -1798,7 +1798,7 @@ fn populate_job_from_job_node(
|
||||
|
||||
// Construct Processes for job continuations (pipelines).
|
||||
for jc in &job_node.continuation {
|
||||
if result != EndExecutionReason::ok {
|
||||
if result != EndExecutionReason::Ok {
|
||||
break;
|
||||
}
|
||||
// Handle the pipe, whose fd may not be the obvious stdout.
|
||||
@@ -1842,7 +1842,7 @@ fn populate_job_from_job_node(
|
||||
processes.last_mut().unwrap().is_last_in_job = true;
|
||||
|
||||
// Return what happened.
|
||||
if result == EndExecutionReason::ok {
|
||||
if result == EndExecutionReason::Ok {
|
||||
// Link up the processes.
|
||||
assert!(!processes.is_empty());
|
||||
*j.processes_mut() = processes.into_boxed_slice();
|
||||
@@ -1944,7 +1944,7 @@ fn profiling_cmd_name_for_redirectable_block(
|
||||
/// Get a redirection from stderr to stdout (i.e. 2>&1).
|
||||
fn get_stderr_merge() -> RedirectionSpec {
|
||||
let stdout_fileno_str = L!("1").to_owned();
|
||||
RedirectionSpec::new(STDERR_FILENO, RedirectionMode::fd, stdout_fileno_str)
|
||||
RedirectionSpec::new(STDERR_FILENO, RedirectionMode::Fd, stdout_fileno_str)
|
||||
}
|
||||
|
||||
/// Decide if a job node should be 'time'd.
|
||||
|
||||
@@ -44,7 +44,7 @@ pub fn new(typ: ParseTokenType) -> Self {
|
||||
is_help_argument: false,
|
||||
is_newline: false,
|
||||
may_be_variable_assignment: false,
|
||||
tok_error: TokenizerError::none,
|
||||
tok_error: TokenizerError::None,
|
||||
source_start: SOURCE_OFFSET_INVALID.try_into().unwrap(),
|
||||
source_length: 0,
|
||||
}
|
||||
@@ -68,7 +68,7 @@ pub fn range(&self) -> SourceRange {
|
||||
}
|
||||
/// Return whether we are a string with the dash prefix set.
|
||||
pub fn is_dash_prefix_string(&self) -> bool {
|
||||
self.typ == ParseTokenType::string && self.has_dash_prefix
|
||||
self.typ == ParseTokenType::String && self.has_dash_prefix
|
||||
}
|
||||
/// Returns a string description of the given parse token.
|
||||
pub fn describe(&self) -> WString {
|
||||
@@ -86,15 +86,13 @@ pub fn user_presentable_description(&self) -> WString {
|
||||
impl From<TokenizerError> for ParseErrorCode {
|
||||
fn from(err: TokenizerError) -> Self {
|
||||
match err {
|
||||
TokenizerError::none => ParseErrorCode::none,
|
||||
TokenizerError::unterminated_quote => ParseErrorCode::tokenizer_unterminated_quote,
|
||||
TokenizerError::unterminated_subshell => {
|
||||
ParseErrorCode::tokenizer_unterminated_subshell
|
||||
}
|
||||
TokenizerError::unterminated_slice => ParseErrorCode::tokenizer_unterminated_slice,
|
||||
TokenizerError::unterminated_escape => ParseErrorCode::tokenizer_unterminated_escape,
|
||||
TokenizerError::None => ParseErrorCode::None,
|
||||
TokenizerError::UnterminatedQuote => ParseErrorCode::TokenizerUnterminatedQuote,
|
||||
TokenizerError::UnterminatedSubshell => ParseErrorCode::TokenizerUnterminatedSubshell,
|
||||
TokenizerError::UnterminatedSlice => ParseErrorCode::TokenizerUnterminatedSlice,
|
||||
TokenizerError::UnterminatedEscape => ParseErrorCode::TokenizerUnterminatedEscape,
|
||||
// To-do: maybe also unbalancing brace?
|
||||
_ => ParseErrorCode::tokenizer_other,
|
||||
_ => ParseErrorCode::TokenizerOther,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,14 +425,14 @@ fn job_or_process_extent(
|
||||
break;
|
||||
}
|
||||
match token.type_ {
|
||||
TokenType::pipe
|
||||
| TokenType::end
|
||||
| TokenType::background
|
||||
| TokenType::andand
|
||||
| TokenType::oror
|
||||
| TokenType::left_brace
|
||||
| TokenType::right_brace
|
||||
if (token.type_ != TokenType::pipe || process) =>
|
||||
TokenType::Pipe
|
||||
| TokenType::End
|
||||
| TokenType::Background
|
||||
| TokenType::AndAnd
|
||||
| TokenType::OrOr
|
||||
| TokenType::LeftBrace
|
||||
| TokenType::RightBrace
|
||||
if (token.type_ != TokenType::Pipe || process) =>
|
||||
{
|
||||
if tok_begin >= pos {
|
||||
finished = true;
|
||||
@@ -475,7 +475,7 @@ pub fn parse_util_token_extent(buff: &wstr, cursor_pos: usize) -> (Range<usize>,
|
||||
let mut tok_end = tok_begin;
|
||||
|
||||
// Calculate end of token.
|
||||
if token.type_ == TokenType::string {
|
||||
if token.type_ == TokenType::String {
|
||||
tok_end += token.length();
|
||||
}
|
||||
|
||||
@@ -489,14 +489,14 @@ pub fn parse_util_token_extent(buff: &wstr, cursor_pos: usize) -> (Range<usize>,
|
||||
|
||||
// If cursor is inside the token, this is the token we are looking for. If so, set
|
||||
// cur_begin and cur_end and break.
|
||||
if token.type_ == TokenType::string && tok_end >= offset_within_cmdsubst {
|
||||
if token.type_ == TokenType::String && tok_end >= offset_within_cmdsubst {
|
||||
cur_begin = cmdsubst_begin + token.offset();
|
||||
cur_end = cur_begin + token.length();
|
||||
break;
|
||||
}
|
||||
|
||||
// Remember previous string token.
|
||||
if token.type_ == TokenType::string {
|
||||
if token.type_ == TokenType::String {
|
||||
prev_begin = cmdsubst_begin + token.offset();
|
||||
prev_end = prev_begin + token.length();
|
||||
}
|
||||
@@ -565,7 +565,7 @@ pub fn parse_util_get_offset(s: &wstr, line: i32, line_offset: isize) -> Option<
|
||||
/// transformation.
|
||||
pub fn parse_util_unescape_wildcards(s: &wstr) -> WString {
|
||||
let mut result = WString::with_capacity(s.len());
|
||||
let unesc_qmark = !feature_test(FeatureFlag::qmark_noglob);
|
||||
let unesc_qmark = !feature_test(FeatureFlag::QuestionMarkNoGlob);
|
||||
|
||||
let mut i = 0;
|
||||
while i < s.len() {
|
||||
@@ -593,7 +593,7 @@ pub fn parse_util_unescape_wildcards(s: &wstr) -> WString {
|
||||
|
||||
/// Return if the given string contains wildcard characters.
|
||||
pub fn parse_util_contains_wildcards(s: &wstr) -> bool {
|
||||
let unesc_qmark = !feature_test(FeatureFlag::qmark_noglob);
|
||||
let unesc_qmark = !feature_test(FeatureFlag::QuestionMarkNoGlob);
|
||||
|
||||
let mut i = 0;
|
||||
while i < s.len() {
|
||||
@@ -622,7 +622,7 @@ pub fn parse_util_contains_wildcards(s: &wstr) -> bool {
|
||||
/// "a*b" to "a\*b".
|
||||
pub fn parse_util_escape_wildcards(s: &wstr) -> WString {
|
||||
let mut result = WString::with_capacity(s.len());
|
||||
let unesc_qmark = !feature_test(FeatureFlag::qmark_noglob);
|
||||
let unesc_qmark = !feature_test(FeatureFlag::QuestionMarkNoGlob);
|
||||
|
||||
for c in s.chars() {
|
||||
if c == '*' {
|
||||
@@ -1063,7 +1063,7 @@ fn visit(&mut self, node: &'a dyn Node) {
|
||||
Kind::Token(node) => {
|
||||
let token_type = node.token_type();
|
||||
let parent_kind = self.parent.unwrap().kind();
|
||||
if matches!(parent_kind, Kind::BeginHeader(_)) && token_type == ParseTokenType::end
|
||||
if matches!(parent_kind, Kind::BeginHeader(_)) && token_type == ParseTokenType::End
|
||||
{
|
||||
// The newline after "begin" is optional, so it is part of the header.
|
||||
// The header is not in the indented block, so indent the newline here.
|
||||
@@ -1141,8 +1141,8 @@ pub fn parse_util_detect_errors(
|
||||
// successfully.
|
||||
parse_errors.retain(|parse_error| {
|
||||
if [
|
||||
ParseErrorCode::tokenizer_unterminated_quote,
|
||||
ParseErrorCode::tokenizer_unterminated_subshell,
|
||||
ParseErrorCode::TokenizerUnterminatedQuote,
|
||||
ParseErrorCode::TokenizerUnterminatedSubshell,
|
||||
]
|
||||
.contains(&parse_error.code)
|
||||
{
|
||||
@@ -1372,7 +1372,7 @@ macro_rules! append_syntax_error_formatted {
|
||||
let mut error = ParseError::default();
|
||||
error.source_start = $source_location;
|
||||
error.source_length = $source_length;
|
||||
error.code = ParseErrorCode::syntax;
|
||||
error.code = ParseErrorCode::Syntax;
|
||||
error.text = $text;
|
||||
errors.push(error);
|
||||
}
|
||||
@@ -1536,7 +1536,7 @@ fn detect_errors_in_job_conjunction(
|
||||
conjunction.source_range().start(),
|
||||
conjunction.source_range().length(),
|
||||
BOOL_AFTER_BACKGROUND_ERROR_MSG,
|
||||
if conjunction.token_type() == ParseTokenType::andand {
|
||||
if conjunction.token_type() == ParseTokenType::AndAnd {
|
||||
L!("&&")
|
||||
} else {
|
||||
L!("||")
|
||||
@@ -1648,16 +1648,16 @@ fn detect_errors_in_decorated_statement(
|
||||
|
||||
// Check our pipeline position.
|
||||
let pipe_pos = if job.continuation.is_empty() {
|
||||
PipelinePosition::none
|
||||
PipelinePosition::None
|
||||
} else if is_same_node(&job.statement, st) {
|
||||
PipelinePosition::first
|
||||
PipelinePosition::First
|
||||
} else {
|
||||
PipelinePosition::subsequent
|
||||
PipelinePosition::Subsequent
|
||||
};
|
||||
|
||||
// Check that we don't try to pipe through exec.
|
||||
let is_in_pipeline = pipe_pos != PipelinePosition::none;
|
||||
if is_in_pipeline && decoration == StatementDecoration::exec {
|
||||
let is_in_pipeline = pipe_pos != PipelinePosition::None;
|
||||
if is_in_pipeline && decoration == StatementDecoration::Exec {
|
||||
errored = append_syntax_error!(
|
||||
parse_errors,
|
||||
source_start,
|
||||
@@ -1670,13 +1670,13 @@ fn detect_errors_in_decorated_statement(
|
||||
// This is a somewhat stale check that 'and' and 'or' are not in pipelines, except at the
|
||||
// beginning. We can't disallow them as commands entirely because we need to support 'and
|
||||
// --help', etc.
|
||||
if pipe_pos == PipelinePosition::subsequent {
|
||||
if pipe_pos == PipelinePosition::Subsequent {
|
||||
// We only reject it if we have no decoration.
|
||||
// `echo foo | command time something`
|
||||
// is entirely fair and valid.
|
||||
// Other current decorations like "exec"
|
||||
// are already forbidden.
|
||||
if dst.decoration() == StatementDecoration::none {
|
||||
if dst.decoration() == StatementDecoration::None {
|
||||
// check if our command is 'and' or 'or'. This is very clumsy; we don't catch e.g. quoted
|
||||
// commands.
|
||||
let command = dst.command.source(buff_src);
|
||||
@@ -1796,7 +1796,7 @@ fn detect_errors_in_decorated_statement(
|
||||
}
|
||||
|
||||
// Check that we don't do an invalid builtin (issue #1252).
|
||||
if !errored && decoration == StatementDecoration::builtin {
|
||||
if !errored && decoration == StatementDecoration::Builtin {
|
||||
let mut command = unexp_command.to_owned();
|
||||
if expand_one(
|
||||
&mut command,
|
||||
|
||||
@@ -714,7 +714,7 @@ pub fn eval_node<T: Node>(
|
||||
EvalRes::new(ProcStatus::from_signal(Signal::new(sig)))
|
||||
} else {
|
||||
let status = ProcStatus::from_exit_code(self.get_last_status());
|
||||
let break_expand = reason == EndExecutionReason::error;
|
||||
let break_expand = reason == EndExecutionReason::Error;
|
||||
EvalRes {
|
||||
status,
|
||||
break_expand,
|
||||
@@ -1901,53 +1901,53 @@ macro_rules! validate {
|
||||
};
|
||||
}
|
||||
|
||||
validate!("echo hello", "echo", "hello", StatementDecoration::none);
|
||||
validate!("echo hello", "echo", "hello", StatementDecoration::None);
|
||||
validate!(
|
||||
"command echo hello",
|
||||
"echo",
|
||||
"hello",
|
||||
StatementDecoration::command
|
||||
StatementDecoration::Command
|
||||
);
|
||||
validate!(
|
||||
"exec echo hello",
|
||||
"echo",
|
||||
"hello",
|
||||
StatementDecoration::exec
|
||||
StatementDecoration::Exec
|
||||
);
|
||||
validate!(
|
||||
"command command hello",
|
||||
"command",
|
||||
"hello",
|
||||
StatementDecoration::command
|
||||
StatementDecoration::Command
|
||||
);
|
||||
validate!(
|
||||
"builtin command hello",
|
||||
"command",
|
||||
"hello",
|
||||
StatementDecoration::builtin
|
||||
StatementDecoration::Builtin
|
||||
);
|
||||
validate!(
|
||||
"command --help",
|
||||
"command",
|
||||
"--help",
|
||||
StatementDecoration::none
|
||||
StatementDecoration::None
|
||||
);
|
||||
validate!("command -h", "command", "-h", StatementDecoration::none);
|
||||
validate!("command", "command", "", StatementDecoration::none);
|
||||
validate!("command -", "command", "-", StatementDecoration::none);
|
||||
validate!("command --", "command", "--", StatementDecoration::none);
|
||||
validate!("command -h", "command", "-h", StatementDecoration::None);
|
||||
validate!("command", "command", "", StatementDecoration::None);
|
||||
validate!("command -", "command", "-", StatementDecoration::None);
|
||||
validate!("command --", "command", "--", StatementDecoration::None);
|
||||
validate!(
|
||||
"builtin --names",
|
||||
"builtin",
|
||||
"--names",
|
||||
StatementDecoration::none
|
||||
StatementDecoration::None
|
||||
);
|
||||
validate!("function", "function", "", StatementDecoration::none);
|
||||
validate!("function", "function", "", StatementDecoration::None);
|
||||
validate!(
|
||||
"function --help",
|
||||
"function",
|
||||
"--help",
|
||||
StatementDecoration::none
|
||||
StatementDecoration::None
|
||||
);
|
||||
|
||||
// Verify that 'function -h' and 'function --help' are plain statements but 'function --foo' is
|
||||
@@ -2010,7 +2010,7 @@ fn test_new_parser_ad_hoc() {
|
||||
Some(&mut errors),
|
||||
);
|
||||
assert!(errors.len() == 1);
|
||||
assert!(errors[0].code == ParseErrorCode::tokenizer_unterminated_subshell);
|
||||
assert!(errors[0].code == ParseErrorCode::TokenizerUnterminatedSubshell);
|
||||
|
||||
errors.clear();
|
||||
ast::parse(
|
||||
@@ -2019,7 +2019,7 @@ fn test_new_parser_ad_hoc() {
|
||||
Some(&mut errors),
|
||||
);
|
||||
assert!(errors.len() == 1);
|
||||
assert!(errors[0].code == ParseErrorCode::tokenizer_unterminated_subshell);
|
||||
assert!(errors[0].code == ParseErrorCode::TokenizerUnterminatedSubshell);
|
||||
|
||||
errors.clear();
|
||||
ast::parse(
|
||||
@@ -2028,7 +2028,7 @@ fn test_new_parser_ad_hoc() {
|
||||
Some(&mut errors),
|
||||
);
|
||||
assert!(errors.len() == 1);
|
||||
assert!(errors[0].code == ParseErrorCode::tokenizer_unterminated_quote);
|
||||
assert!(errors[0].code == ParseErrorCode::TokenizerUnterminatedQuote);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2047,24 +2047,24 @@ macro_rules! validate {
|
||||
};
|
||||
}
|
||||
|
||||
validate!("echo 'abc", ParseErrorCode::tokenizer_unterminated_quote);
|
||||
validate!("'", ParseErrorCode::tokenizer_unterminated_quote);
|
||||
validate!("echo (abc", ParseErrorCode::tokenizer_unterminated_subshell);
|
||||
validate!("echo 'abc", ParseErrorCode::TokenizerUnterminatedQuote);
|
||||
validate!("'", ParseErrorCode::TokenizerUnterminatedQuote);
|
||||
validate!("echo (abc", ParseErrorCode::TokenizerUnterminatedSubshell);
|
||||
|
||||
validate!("end", ParseErrorCode::unbalancing_end);
|
||||
validate!("echo hi ; end", ParseErrorCode::unbalancing_end);
|
||||
validate!("end", ParseErrorCode::UnbalancingEnd);
|
||||
validate!("echo hi ; end", ParseErrorCode::UnbalancingEnd);
|
||||
|
||||
validate!("else", ParseErrorCode::unbalancing_else);
|
||||
validate!("if true ; end ; else", ParseErrorCode::unbalancing_else);
|
||||
validate!("else", ParseErrorCode::UnbalancingElse);
|
||||
validate!("if true ; end ; else", ParseErrorCode::UnbalancingElse);
|
||||
|
||||
validate!("case", ParseErrorCode::unbalancing_case);
|
||||
validate!("if true ; case ; end", ParseErrorCode::unbalancing_case);
|
||||
validate!("case", ParseErrorCode::UnbalancingCase);
|
||||
validate!("if true ; case ; end", ParseErrorCode::UnbalancingCase);
|
||||
|
||||
validate!("begin ; }", ParseErrorCode::unbalancing_brace);
|
||||
validate!("begin ; }", ParseErrorCode::UnbalancingBrace);
|
||||
|
||||
validate!("true | and", ParseErrorCode::andor_in_pipeline);
|
||||
validate!("true | and", ParseErrorCode::AndOrInPipeline);
|
||||
|
||||
validate!("a=", ParseErrorCode::bare_variable_assignment);
|
||||
validate!("a=", ParseErrorCode::BareVariableAssignment);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2416,7 +2416,7 @@ fn test_traversal_parent_panics() {
|
||||
while let Some(node) = traversal.next() {
|
||||
if let Kind::DecoratedStatement(_) = node.kind() {
|
||||
decorated_statement = Some(node);
|
||||
} else if node.as_token().map(|t| t.token_type()) == Some(ParseTokenType::end) {
|
||||
} else if node.as_token().map(|t| t.token_type()) == Some(ParseTokenType::End) {
|
||||
// should panic as the decorated_statement is not on the stack.
|
||||
let _ = traversal.parent(decorated_statement.unwrap());
|
||||
}
|
||||
|
||||
32
src/path.rs
32
src/path.rs
@@ -66,11 +66,11 @@ pub fn path_get_cache() -> Option<WString> {
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum DirRemoteness {
|
||||
/// directory status is unknown
|
||||
unknown,
|
||||
Unknown,
|
||||
/// directory is known local
|
||||
local,
|
||||
Local,
|
||||
/// directory is known remote
|
||||
remote,
|
||||
Remote,
|
||||
}
|
||||
|
||||
/// Return the remoteness of the fish data directory.
|
||||
@@ -99,7 +99,7 @@ pub fn path_emit_config_directory_messages(vars: &EnvStack) {
|
||||
vars,
|
||||
);
|
||||
}
|
||||
if data.remoteness == DirRemoteness::remote {
|
||||
if data.remoteness == DirRemoteness::Remote {
|
||||
FLOG!(path, "data path appears to be on a network volume");
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ pub fn path_emit_config_directory_messages(vars: &EnvStack) {
|
||||
vars,
|
||||
);
|
||||
}
|
||||
if config.remoteness == DirRemoteness::remote {
|
||||
if config.remoteness == DirRemoteness::Remote {
|
||||
FLOG!(path, "config path appears to be on a network volume");
|
||||
}
|
||||
}
|
||||
@@ -600,7 +600,7 @@ fn make_base_directory(xdg_var: &wstr, non_xdg_homepath: &wstr) -> BaseDirectory
|
||||
|
||||
return BaseDirectory {
|
||||
path: bytes2wcstring(build_dir.as_os_str().as_bytes()),
|
||||
remoteness: DirRemoteness::unknown,
|
||||
remoteness: DirRemoteness::Unknown,
|
||||
used_xdg: false,
|
||||
err,
|
||||
};
|
||||
@@ -625,7 +625,7 @@ fn make_base_directory(xdg_var: &wstr, non_xdg_homepath: &wstr) -> BaseDirectory
|
||||
|
||||
set_errno(Errno(0));
|
||||
let err;
|
||||
let mut remoteness = DirRemoteness::unknown;
|
||||
let mut remoteness = DirRemoteness::Unknown;
|
||||
if path.is_empty() {
|
||||
err = ENOENT;
|
||||
} else if let Err(io_error) = create_dir_all_with_mode(wcs2osstring(&path), 0o700) {
|
||||
@@ -662,7 +662,7 @@ pub fn path_remoteness(path: &wstr) -> DirRemoteness {
|
||||
{
|
||||
let mut buf = MaybeUninit::uninit();
|
||||
if unsafe { libc::statfs(narrow.as_ptr(), buf.as_mut_ptr()) } < 0 {
|
||||
return DirRemoteness::unknown;
|
||||
return DirRemoteness::Unknown;
|
||||
}
|
||||
let buf = unsafe { buf.assume_init() };
|
||||
// Linux has constants for these like NFS_SUPER_MAGIC, SMB_SUPER_MAGIC, CIFS_MAGIC_NUMBER but
|
||||
@@ -686,9 +686,9 @@ pub fn path_remoteness(path: &wstr) -> DirRemoteness {
|
||||
0x013111A7 | 0x013111A8 | // IBRIX. Undocumented.
|
||||
0x65735546 | // FUSE_SUPER_MAGIC
|
||||
0xA501FCF5 // VXFS_SUPER_MAGIC
|
||||
=> DirRemoteness::remote,
|
||||
=> DirRemoteness::Remote,
|
||||
_ => {
|
||||
DirRemoteness::unknown
|
||||
DirRemoteness::Unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -698,16 +698,16 @@ pub fn path_remoteness(path: &wstr) -> DirRemoteness {
|
||||
{
|
||||
let mut buf = MaybeUninit::uninit();
|
||||
if unsafe { libc::statvfs(narrow.as_ptr(), buf.as_mut_ptr()) } < 0 {
|
||||
return DirRemoteness::unknown;
|
||||
return DirRemoteness::Unknown;
|
||||
}
|
||||
let buf = unsafe { buf.assume_init() };
|
||||
#[allow(clippy::useless_conversion)]
|
||||
let flags = buf.f_flag as u64;
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
if flags & (libc::MNT_LOCAL as u64) != 0 {
|
||||
DirRemoteness::local
|
||||
DirRemoteness::Local
|
||||
} else {
|
||||
DirRemoteness::remote
|
||||
DirRemoteness::Remote
|
||||
}
|
||||
}
|
||||
|
||||
@@ -715,7 +715,7 @@ pub fn path_remoteness(path: &wstr) -> DirRemoteness {
|
||||
{
|
||||
let mut buf = MaybeUninit::uninit();
|
||||
if unsafe { libc::statfs(narrow.as_ptr(), buf.as_mut_ptr()) } < 0 {
|
||||
return DirRemoteness::unknown;
|
||||
return DirRemoteness::Unknown;
|
||||
}
|
||||
let buf = unsafe { buf.assume_init() };
|
||||
// statfs::f_flags types differ.
|
||||
@@ -723,9 +723,9 @@ pub fn path_remoteness(path: &wstr) -> DirRemoteness {
|
||||
let flags = buf.f_flags as u64;
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
if flags & (libc::MNT_LOCAL as u64) != 0 {
|
||||
DirRemoteness::local
|
||||
DirRemoteness::Local
|
||||
} else {
|
||||
DirRemoteness::remote
|
||||
DirRemoteness::Remote
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
src/proc.rs
10
src/proc.rs
@@ -252,7 +252,7 @@ pub fn exited(&self) -> bool {
|
||||
/// Mark this process as having exited with the given `status`.
|
||||
pub fn mark_exited(&self, status: ProcStatus) {
|
||||
self.status.set(status).expect("Status already set");
|
||||
topic_monitor_principal().post(Topic::internal_exit);
|
||||
topic_monitor_principal().post(Topic::InternalExit);
|
||||
FLOG!(
|
||||
proc_internal_proc,
|
||||
"Internal proc",
|
||||
@@ -1191,13 +1191,13 @@ fn process_mark_finished_children(parser: &Parser, block_ok: bool) {
|
||||
|
||||
if proc.has_pid() {
|
||||
// Reaps with a pid.
|
||||
reapgens.set_min_from(Topic::sigchld, &proc.gens);
|
||||
reapgens.set_min_from(Topic::sighupint, &proc.gens);
|
||||
reapgens.set_min_from(Topic::SigChld, &proc.gens);
|
||||
reapgens.set_min_from(Topic::SigHupInt, &proc.gens);
|
||||
}
|
||||
if proc.internal_proc.borrow().is_some() {
|
||||
// Reaps with an internal process.
|
||||
reapgens.set_min_from(Topic::internal_exit, &proc.gens);
|
||||
reapgens.set_min_from(Topic::sighupint, &proc.gens);
|
||||
reapgens.set_min_from(Topic::InternalExit, &proc.gens);
|
||||
reapgens.set_min_from(Topic::SigHupInt, &proc.gens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ fn append_matches_from_search(&mut self) -> bool {
|
||||
|
||||
let mut local_tokens = vec![];
|
||||
while let Some(token) = tok.next() {
|
||||
if token.type_ != TokenType::string {
|
||||
if token.type_ != TokenType::String {
|
||||
continue;
|
||||
}
|
||||
let text = tok.text_of(&token);
|
||||
|
||||
@@ -239,7 +239,7 @@ fn redirect_tty_after_sighup() {
|
||||
}
|
||||
|
||||
fn querying_allowed(vars: &dyn Environment) -> bool {
|
||||
future_feature_flags::test(FeatureFlag::query_term)
|
||||
future_feature_flags::test(FeatureFlag::QueryTerm)
|
||||
&& !is_dumb()
|
||||
&& {
|
||||
// TODO(term-workaround)
|
||||
@@ -4122,7 +4122,7 @@ fn forward_token(&self, autosuggest: bool) -> Option<usize> {
|
||||
|
||||
let cmdsubst_range = parse_util_cmdsubst_extent(&buffer, pos);
|
||||
for token in Tokenizer::new(&buffer[cmdsubst_range.clone()], TOK_ACCEPT_UNFINISHED) {
|
||||
if token.type_ != TokenType::string {
|
||||
if token.type_ != TokenType::String {
|
||||
continue;
|
||||
}
|
||||
let tok_end = cmdsubst_range.start + token.end();
|
||||
@@ -4138,7 +4138,7 @@ fn forward_token(&self, autosuggest: bool) -> Option<usize> {
|
||||
fn text_ends_in_comment(text: &wstr) -> bool {
|
||||
Tokenizer::new(text, TOK_ACCEPT_UNFINISHED | TOK_SHOW_COMMENTS)
|
||||
.last()
|
||||
.is_some_and(|token| token.type_ == TokenType::comment)
|
||||
.is_some_and(|token| token.type_ == TokenType::Comment)
|
||||
}
|
||||
|
||||
impl<'a> Reader<'a> {
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum RedirectionMode {
|
||||
overwrite, // normal redirection: > file.txt
|
||||
append, // appending redirection: >> file.txt
|
||||
input, // input redirection: < file.txt
|
||||
try_input, // try-input redirection: <? file.txt
|
||||
fd, // fd redirection: 2>&1
|
||||
noclob, // noclobber redirection: >? file.txt
|
||||
Overwrite, // normal redirection: > file.txt
|
||||
Append, // appending redirection: >> file.txt
|
||||
Input, // input redirection: < file.txt
|
||||
TryInput, // try-input redirection: <? file.txt
|
||||
Fd, // fd redirection: 2>&1
|
||||
NoClob, // noclobber redirection: >? file.txt
|
||||
}
|
||||
|
||||
/// A type that represents the action dup2(src, target).
|
||||
@@ -36,10 +36,10 @@ impl RedirectionMode {
|
||||
/// The open flags for this redirection mode.
|
||||
pub fn oflags(self) -> Option<OFlag> {
|
||||
match self {
|
||||
RedirectionMode::append => Some(OFlag::O_CREAT | OFlag::O_APPEND | OFlag::O_WRONLY),
|
||||
RedirectionMode::overwrite => Some(OFlag::O_CREAT | OFlag::O_WRONLY | OFlag::O_TRUNC),
|
||||
RedirectionMode::noclob => Some(OFlag::O_CREAT | OFlag::O_EXCL | OFlag::O_WRONLY),
|
||||
RedirectionMode::input | RedirectionMode::try_input => Some(OFlag::O_RDONLY),
|
||||
RedirectionMode::Append => Some(OFlag::O_CREAT | OFlag::O_APPEND | OFlag::O_WRONLY),
|
||||
RedirectionMode::Overwrite => Some(OFlag::O_CREAT | OFlag::O_WRONLY | OFlag::O_TRUNC),
|
||||
RedirectionMode::NoClob => Some(OFlag::O_CREAT | OFlag::O_EXCL | OFlag::O_WRONLY),
|
||||
RedirectionMode::Input | RedirectionMode::TryInput => Some(OFlag::O_RDONLY),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ pub fn new(fd: RawFd, mode: RedirectionMode, target: WString) -> Self {
|
||||
}
|
||||
/// Return if this is a close-type redirection.
|
||||
pub fn is_close(&self) -> bool {
|
||||
self.mode == RedirectionMode::fd && self.target == "-"
|
||||
self.mode == RedirectionMode::Fd && self.target == "-"
|
||||
}
|
||||
|
||||
/// Attempt to parse target as an fd.
|
||||
|
||||
@@ -86,7 +86,7 @@ extern "C" fn fish_signal_handler(
|
||||
reader_sighup();
|
||||
safe_mark_tty_invalid();
|
||||
}
|
||||
topic_monitor_principal().post(Topic::sighupint);
|
||||
topic_monitor_principal().post(Topic::SigHupInt);
|
||||
}
|
||||
libc::SIGTERM => {
|
||||
// Handle sigterm. The only thing we do is restore the front process ID and disable protocols, then die.
|
||||
@@ -106,11 +106,11 @@ extern "C" fn fish_signal_handler(
|
||||
CANCELLATION_SIGNAL.store(libc::SIGINT, Ordering::Relaxed);
|
||||
}
|
||||
reader_handle_sigint();
|
||||
topic_monitor_principal().post(Topic::sighupint);
|
||||
topic_monitor_principal().post(Topic::SigHupInt);
|
||||
}
|
||||
libc::SIGCHLD => {
|
||||
// A child process stopped or exited.
|
||||
topic_monitor_principal().post(Topic::sigchld);
|
||||
topic_monitor_principal().post(Topic::SigChld);
|
||||
}
|
||||
libc::SIGALRM => {
|
||||
// We have a sigalarm handler that does nothing. This is used in the signal torture
|
||||
@@ -324,7 +324,7 @@ pub fn new(topic: Topic) -> Self {
|
||||
|
||||
/// Create a new checker for SIGHUP and SIGINT.
|
||||
pub fn new_sighupint() -> Self {
|
||||
Self::new(Topic::sighupint)
|
||||
Self::new(Topic::SigHupInt)
|
||||
}
|
||||
|
||||
/// Check if a sigint has been delivered since the last call to check(), or since the detector
|
||||
|
||||
@@ -225,7 +225,7 @@ fn maybe_terminfo(
|
||||
}
|
||||
|
||||
pub(crate) fn use_terminfo() -> bool {
|
||||
!future_feature_flags::test(FeatureFlag::ignore_terminfo) && TERM.lock().unwrap().is_some()
|
||||
!future_feature_flags::test(FeatureFlag::IgnoreTerminfo) && TERM.lock().unwrap().is_some()
|
||||
}
|
||||
|
||||
fn underline_mode(out: &mut impl Output, style: UnderlineStyle) -> bool {
|
||||
@@ -394,7 +394,7 @@ fn osc_0_or_1_terminal_title(out: &mut impl Output, is_1: bool, title: &[WString
|
||||
}
|
||||
|
||||
fn osc_133_prompt_start(out: &mut impl Output) -> bool {
|
||||
if !future_feature_flags::test(FeatureFlag::mark_prompt) {
|
||||
if !future_feature_flags::test(FeatureFlag::MarkPrompt) {
|
||||
return false;
|
||||
}
|
||||
static TEST_BALLOON: OnceCell<()> = OnceCell::new();
|
||||
@@ -407,7 +407,7 @@ fn osc_133_prompt_start(out: &mut impl Output) -> bool {
|
||||
}
|
||||
|
||||
fn osc_133_prompt_end(out: &mut impl Output) -> bool {
|
||||
if !future_feature_flags::test(FeatureFlag::mark_prompt) {
|
||||
if !future_feature_flags::test(FeatureFlag::MarkPrompt) {
|
||||
return false;
|
||||
}
|
||||
write_to_output!(out, "\x1b]133;B\x07");
|
||||
@@ -415,7 +415,7 @@ fn osc_133_prompt_end(out: &mut impl Output) -> bool {
|
||||
}
|
||||
|
||||
fn osc_133_command_start(out: &mut impl Output, command: &wstr) -> bool {
|
||||
if !future_feature_flags::test(FeatureFlag::mark_prompt) {
|
||||
if !future_feature_flags::test(FeatureFlag::MarkPrompt) {
|
||||
return false;
|
||||
}
|
||||
write_to_output!(
|
||||
@@ -427,7 +427,7 @@ fn osc_133_command_start(out: &mut impl Output, command: &wstr) -> bool {
|
||||
}
|
||||
|
||||
fn osc_133_command_finished(out: &mut impl Output, exit_status: libc::c_int) -> bool {
|
||||
if !future_feature_flags::test(FeatureFlag::mark_prompt) {
|
||||
if !future_feature_flags::test(FeatureFlag::MarkPrompt) {
|
||||
return false;
|
||||
}
|
||||
write_to_output!(out, "\x1b]133;D;{}\x07", exit_status);
|
||||
|
||||
230
src/tokenizer.rs
230
src/tokenizer.rs
@@ -17,45 +17,45 @@
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum TokenType {
|
||||
/// Error reading token
|
||||
error,
|
||||
Error,
|
||||
/// String token
|
||||
string,
|
||||
String,
|
||||
/// Pipe token
|
||||
pipe,
|
||||
Pipe,
|
||||
/// && token
|
||||
andand,
|
||||
AndAnd,
|
||||
/// || token
|
||||
oror,
|
||||
OrOr,
|
||||
/// End token (semicolon or newline, not literal end)
|
||||
end,
|
||||
End,
|
||||
/// opening brace of a compound statement
|
||||
left_brace,
|
||||
LeftBrace,
|
||||
/// closing brace of a compound statement
|
||||
right_brace,
|
||||
RightBrace,
|
||||
/// redirection token
|
||||
redirect,
|
||||
Redirect,
|
||||
/// send job to bg token
|
||||
background,
|
||||
Background,
|
||||
/// comment token
|
||||
comment,
|
||||
Comment,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum TokenizerError {
|
||||
none,
|
||||
unterminated_quote,
|
||||
unterminated_subshell,
|
||||
unterminated_slice,
|
||||
unterminated_escape,
|
||||
invalid_redirect,
|
||||
invalid_pipe,
|
||||
invalid_pipe_ampersand,
|
||||
closing_unopened_subshell,
|
||||
illegal_slice,
|
||||
closing_unopened_brace,
|
||||
unterminated_brace,
|
||||
expected_pclose_found_bclose,
|
||||
expected_bclose_found_pclose,
|
||||
None,
|
||||
UnterminatedQuote,
|
||||
UnterminatedSubshell,
|
||||
UnterminatedSlice,
|
||||
UnterminatedEscape,
|
||||
InvalidRedirect,
|
||||
InvalidPipe,
|
||||
InvalidPipeAmpersand,
|
||||
ClosingUnopenedSubshell,
|
||||
IllegalSlice,
|
||||
ClosingUnopenedBrace,
|
||||
UnterminatedBrace,
|
||||
ExpectedPcloseFoundBclose,
|
||||
ExpectedBcloseFoundPclose,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -159,44 +159,44 @@ fn bitor_assign(&mut self, rhs: Self) {
|
||||
impl From<TokenizerError> for &'static wstr {
|
||||
fn from(err: TokenizerError) -> Self {
|
||||
match err {
|
||||
TokenizerError::none => L!(""),
|
||||
TokenizerError::unterminated_quote => {
|
||||
TokenizerError::None => L!(""),
|
||||
TokenizerError::UnterminatedQuote => {
|
||||
wgettext!("Unexpected end of string, quotes are not balanced")
|
||||
}
|
||||
TokenizerError::unterminated_subshell => {
|
||||
TokenizerError::UnterminatedSubshell => {
|
||||
wgettext!("Unexpected end of string, expecting ')'")
|
||||
}
|
||||
TokenizerError::unterminated_slice => {
|
||||
TokenizerError::UnterminatedSlice => {
|
||||
wgettext!("Unexpected end of string, square brackets do not match")
|
||||
}
|
||||
TokenizerError::unterminated_escape => {
|
||||
TokenizerError::UnterminatedEscape => {
|
||||
wgettext!("Unexpected end of string, incomplete escape sequence")
|
||||
}
|
||||
TokenizerError::invalid_redirect => {
|
||||
TokenizerError::InvalidRedirect => {
|
||||
wgettext!("Invalid input/output redirection")
|
||||
}
|
||||
TokenizerError::invalid_pipe => {
|
||||
TokenizerError::InvalidPipe => {
|
||||
wgettext!("Cannot use stdin (fd 0) as pipe output")
|
||||
}
|
||||
TokenizerError::invalid_pipe_ampersand => {
|
||||
TokenizerError::InvalidPipeAmpersand => {
|
||||
wgettext!("|& is not valid. In fish, use &| to pipe both stdout and stderr.")
|
||||
}
|
||||
TokenizerError::closing_unopened_subshell => {
|
||||
TokenizerError::ClosingUnopenedSubshell => {
|
||||
wgettext!("Unexpected ')' for unopened parenthesis")
|
||||
}
|
||||
TokenizerError::illegal_slice => {
|
||||
TokenizerError::IllegalSlice => {
|
||||
wgettext!("Unexpected '[' at this location")
|
||||
}
|
||||
TokenizerError::closing_unopened_brace => {
|
||||
TokenizerError::ClosingUnopenedBrace => {
|
||||
wgettext!("Unexpected '}' for unopened brace")
|
||||
}
|
||||
TokenizerError::unterminated_brace => {
|
||||
TokenizerError::UnterminatedBrace => {
|
||||
wgettext!("Unexpected end of string, incomplete parameter expansion")
|
||||
}
|
||||
TokenizerError::expected_pclose_found_bclose => {
|
||||
TokenizerError::ExpectedPcloseFoundBclose => {
|
||||
wgettext!("Unexpected '}' found, expecting ')'")
|
||||
}
|
||||
TokenizerError::expected_bclose_found_pclose => {
|
||||
TokenizerError::ExpectedBcloseFoundPclose => {
|
||||
wgettext!("Unexpected ')' found, expecting '}'")
|
||||
}
|
||||
}
|
||||
@@ -217,7 +217,7 @@ fn new(r#type: TokenType) -> Tok {
|
||||
length: 0,
|
||||
error_offset_within_token: SOURCE_OFFSET_INVALID.try_into().unwrap(),
|
||||
error_length: 0,
|
||||
error: TokenizerError::none,
|
||||
error: TokenizerError::None,
|
||||
is_unterminated_brace: false,
|
||||
type_: r#type,
|
||||
}
|
||||
@@ -370,7 +370,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
|
||||
// Maybe return the comment.
|
||||
if self.show_comments {
|
||||
let mut result = Tok::new(TokenType::comment);
|
||||
let mut result = Tok::new(TokenType::Comment);
|
||||
result.offset = comment_start as u32;
|
||||
result.length = comment_len as u32;
|
||||
return Some(result);
|
||||
@@ -403,7 +403,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
'\r'| // carriage-return
|
||||
'\n'| // newline
|
||||
';'=> {
|
||||
let mut result = Tok::new(TokenType::end);
|
||||
let mut result = Tok::new(TokenType::End);
|
||||
result.offset = start_pos as u32;
|
||||
result.length = 1;
|
||||
self.token_cursor += 1;
|
||||
@@ -425,7 +425,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
.is_some_and(|parser| parser.at_command_position) =>
|
||||
{
|
||||
self.brace_statement_parser.as_mut().unwrap().unclosed_brace_statements += 1;
|
||||
let mut result = Tok::new(TokenType::left_brace);
|
||||
let mut result = Tok::new(TokenType::LeftBrace);
|
||||
result.offset = start_pos as u32;
|
||||
result.length = 1;
|
||||
self.token_cursor += 1;
|
||||
@@ -437,7 +437,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
.map(|parser| &mut parser.unclosed_brace_statements);
|
||||
if brace_count.as_ref().is_none_or(|count| **count == 0) {
|
||||
return Some(self.call_error(
|
||||
TokenizerError::closing_unopened_brace,
|
||||
TokenizerError::ClosingUnopenedBrace,
|
||||
self.token_cursor,
|
||||
self.token_cursor,
|
||||
Some(1),
|
||||
@@ -445,7 +445,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
));
|
||||
}
|
||||
brace_count.map(|count| *count -= 1);
|
||||
let mut result = Tok::new(TokenType::right_brace);
|
||||
let mut result = Tok::new(TokenType::RightBrace);
|
||||
result.offset = start_pos as u32;
|
||||
result.length = 1;
|
||||
self.token_cursor += 1;
|
||||
@@ -454,7 +454,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
'&'=> {
|
||||
if next_char == Some('&') {
|
||||
// && is and.
|
||||
let mut result = Tok::new(TokenType::andand);
|
||||
let mut result = Tok::new(TokenType::AndAnd);
|
||||
result.offset = start_pos as u32;
|
||||
result.length = 2;
|
||||
self.token_cursor += 2;
|
||||
@@ -471,7 +471,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
at_cmd_pos = next_char == Some('|');
|
||||
Some(result)
|
||||
} else {
|
||||
let mut result = Tok::new(TokenType::background);
|
||||
let mut result = Tok::new(TokenType::Background);
|
||||
result.offset = start_pos as u32;
|
||||
result.length = 1;
|
||||
self.token_cursor += 1;
|
||||
@@ -482,7 +482,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
'|'=> {
|
||||
if next_char == Some('|') {
|
||||
// || is or.
|
||||
let mut result=Tok::new(TokenType::oror);
|
||||
let mut result=Tok::new(TokenType::OrOr);
|
||||
result.offset = start_pos as u32;
|
||||
result.length = 2;
|
||||
self.token_cursor += 2;
|
||||
@@ -490,7 +490,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
Some(result)
|
||||
} else if next_char == Some('&') {
|
||||
// |& is a bashism; in fish it's &|.
|
||||
Some(self.call_error(TokenizerError::invalid_pipe_ampersand,
|
||||
Some(self.call_error(TokenizerError::InvalidPipeAmpersand,
|
||||
self.token_cursor, self.token_cursor, Some(2), 2))
|
||||
} else {
|
||||
let pipe = PipeOrRedir::try_from(buff).
|
||||
@@ -510,7 +510,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
match PipeOrRedir::try_from(buff) {
|
||||
Ok(redir_or_pipe) => {
|
||||
if redir_or_pipe.fd < 0 {
|
||||
Some(self.call_error(TokenizerError::invalid_redirect, self.token_cursor,
|
||||
Some(self.call_error(TokenizerError::InvalidRedirect, self.token_cursor,
|
||||
self.token_cursor,
|
||||
Some(redir_or_pipe.consumed),
|
||||
redir_or_pipe.consumed))
|
||||
@@ -522,7 +522,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
Err(()) => Some(self.call_error(TokenizerError::invalid_redirect, self.token_cursor,
|
||||
Err(()) => Some(self.call_error(TokenizerError::InvalidRedirect, self.token_cursor,
|
||||
self.token_cursor,
|
||||
Some(0),
|
||||
0))
|
||||
@@ -543,7 +543,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
// tSome(hat fd 0 may be -1, indicating overflow; but we don't treat that as a
|
||||
// tokenizer error.
|
||||
if redir_or_pipe.is_pipe && redir_or_pipe.fd == 0 {
|
||||
Some(self.call_error(TokenizerError::invalid_pipe, error_location,
|
||||
Some(self.call_error(TokenizerError::InvalidPipe, error_location,
|
||||
error_location, Some(redir_or_pipe.consumed),
|
||||
redir_or_pipe.consumed))
|
||||
}
|
||||
@@ -563,7 +563,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
.is_some_and(|parser| parser.at_command_position) && {
|
||||
let text = self.text_of(&s);
|
||||
parser_keywords_is_subcommand(&unescape_keyword(
|
||||
TokenType::string,
|
||||
TokenType::String,
|
||||
text)
|
||||
) ||
|
||||
variable_assignment_equals_pos(text).is_some()
|
||||
@@ -606,7 +606,7 @@ fn call_error(
|
||||
error_len: usize,
|
||||
) -> Tok {
|
||||
assert!(
|
||||
error_type != TokenizerError::none,
|
||||
error_type != TokenizerError::None,
|
||||
"TokenizerError::none passed to call_error"
|
||||
);
|
||||
assert!(error_loc >= token_start, "Invalid error location");
|
||||
@@ -632,7 +632,7 @@ fn call_error(
|
||||
error_length: error_len as u32,
|
||||
error: error_type,
|
||||
is_unterminated_brace: false,
|
||||
type_: TokenType::error,
|
||||
type_: TokenType::Error,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -702,7 +702,7 @@ fn process_opening_quote(
|
||||
} else if c == ')' {
|
||||
if expecting.last() == Some(&'}') {
|
||||
return self.call_error(
|
||||
TokenizerError::expected_bclose_found_pclose,
|
||||
TokenizerError::ExpectedBcloseFoundPclose,
|
||||
self.token_cursor,
|
||||
self.token_cursor,
|
||||
Some(1),
|
||||
@@ -711,7 +711,7 @@ fn process_opening_quote(
|
||||
}
|
||||
if paran_offsets.pop().is_none() {
|
||||
return self.call_error(
|
||||
TokenizerError::closing_unopened_subshell,
|
||||
TokenizerError::ClosingUnopenedSubshell,
|
||||
self.token_cursor,
|
||||
self.token_cursor,
|
||||
Some(1),
|
||||
@@ -732,7 +732,7 @@ fn process_opening_quote(
|
||||
{
|
||||
if !self.accept_unfinished {
|
||||
return self.call_error(
|
||||
TokenizerError::unterminated_quote,
|
||||
TokenizerError::UnterminatedQuote,
|
||||
buff_start,
|
||||
error_loc,
|
||||
None,
|
||||
@@ -745,7 +745,7 @@ fn process_opening_quote(
|
||||
} else if c == '}' {
|
||||
if expecting.last() == Some(&')') {
|
||||
return self.call_error(
|
||||
TokenizerError::expected_pclose_found_bclose,
|
||||
TokenizerError::ExpectedPcloseFoundBclose,
|
||||
self.token_cursor,
|
||||
self.token_cursor,
|
||||
Some(1),
|
||||
@@ -780,7 +780,7 @@ fn process_opening_quote(
|
||||
{
|
||||
if !self.accept_unfinished {
|
||||
return self.call_error(
|
||||
TokenizerError::unterminated_quote,
|
||||
TokenizerError::UnterminatedQuote,
|
||||
buff_start,
|
||||
error_loc,
|
||||
None,
|
||||
@@ -817,7 +817,7 @@ fn process_opening_quote(
|
||||
// (except for TOK_MODE_CHAR_ESCAPE, which is one long by definition)
|
||||
if mode & TOK_MODE_CHAR_ESCAPE {
|
||||
return self.call_error(
|
||||
TokenizerError::unterminated_escape,
|
||||
TokenizerError::UnterminatedEscape,
|
||||
buff_start,
|
||||
self.token_cursor - 1,
|
||||
None,
|
||||
@@ -825,7 +825,7 @@ fn process_opening_quote(
|
||||
);
|
||||
} else if mode & TOK_MODE_ARRAY_BRACKETS {
|
||||
return self.call_error(
|
||||
TokenizerError::unterminated_slice,
|
||||
TokenizerError::UnterminatedSlice,
|
||||
buff_start,
|
||||
slice_offset,
|
||||
None,
|
||||
@@ -835,7 +835,7 @@ fn process_opening_quote(
|
||||
let offset_of_open_paran = *paran_offsets.last().expect("paran_offsets is empty");
|
||||
|
||||
return self.call_error(
|
||||
TokenizerError::unterminated_subshell,
|
||||
TokenizerError::UnterminatedSubshell,
|
||||
buff_start,
|
||||
offset_of_open_paran,
|
||||
None,
|
||||
@@ -845,7 +845,7 @@ fn process_opening_quote(
|
||||
let offset_of_open_brace = *brace_offsets.last().expect("brace_offsets is empty");
|
||||
|
||||
return self.call_error(
|
||||
TokenizerError::unterminated_brace,
|
||||
TokenizerError::UnterminatedBrace,
|
||||
buff_start,
|
||||
offset_of_open_brace,
|
||||
None,
|
||||
@@ -856,7 +856,7 @@ fn process_opening_quote(
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = Tok::new(TokenType::string);
|
||||
let mut result = Tok::new(TokenType::String);
|
||||
result.set_offset(buff_start);
|
||||
result.set_length(self.token_cursor - buff_start);
|
||||
result.is_unterminated_brace = mode & TOK_MODE_CURLY_BRACES;
|
||||
@@ -897,7 +897,7 @@ fn tok_is_string_character(c: char, next: Option<char>) -> bool {
|
||||
// Unconditional separators.
|
||||
'\0' | ' ' | '\n' | '|' | '\t' | ';' | '\r' | '<' | '>' => false,
|
||||
'&' => {
|
||||
if feature_test(FeatureFlag::ampersand_nobg_in_token) {
|
||||
if feature_test(FeatureFlag::AmpersandNoBgInToken) {
|
||||
// Unlike in other shells, '&' is not special if followed by a string character.
|
||||
next.map(|nc| tok_is_string_character(nc, None))
|
||||
.unwrap_or(false)
|
||||
@@ -957,7 +957,7 @@ pub fn is_token_delimiter(c: char, next: Option<char>) -> bool {
|
||||
pub fn tok_command(str: &wstr) -> WString {
|
||||
let mut t = Tokenizer::new(str, TokFlags(0));
|
||||
while let Some(token) = t.next() {
|
||||
if token.type_ != TokenType::string {
|
||||
if token.type_ != TokenType::String {
|
||||
return WString::new();
|
||||
}
|
||||
let text = t.text_of(&token);
|
||||
@@ -1021,7 +1021,7 @@ fn try_from(buff: &wstr) -> Result<PipeOrRedir, ()> {
|
||||
let mut result = PipeOrRedir {
|
||||
fd: -1,
|
||||
is_pipe: false,
|
||||
mode: RedirectionMode::overwrite,
|
||||
mode: RedirectionMode::Overwrite,
|
||||
stderr_merge: false,
|
||||
consumed: 0,
|
||||
};
|
||||
@@ -1042,7 +1042,7 @@ fn try_from(buff: &wstr) -> Result<PipeOrRedir, ()> {
|
||||
'>' => {
|
||||
consume(&mut cursor, '>');
|
||||
if try_consume(&mut cursor, '>') {
|
||||
result.mode = RedirectionMode::append;
|
||||
result.mode = RedirectionMode::Append;
|
||||
}
|
||||
if try_consume(&mut cursor, '|') {
|
||||
// Note we differ from bash here.
|
||||
@@ -1061,7 +1061,7 @@ fn try_from(buff: &wstr) -> Result<PipeOrRedir, ()> {
|
||||
// This is a redirection to an fd.
|
||||
// Note that we allow ">>&", but it's still just writing to the fd - "appending" to
|
||||
// it doesn't make sense.
|
||||
result.mode = RedirectionMode::fd;
|
||||
result.mode = RedirectionMode::Fd;
|
||||
result.fd = if has_fd {
|
||||
parse_fd(fd_buff) // like 1>&2
|
||||
} else {
|
||||
@@ -1074,26 +1074,26 @@ fn try_from(buff: &wstr) -> Result<PipeOrRedir, ()> {
|
||||
} else {
|
||||
STDOUT_FILENO // like > file.txt
|
||||
};
|
||||
if result.mode != RedirectionMode::append {
|
||||
result.mode = RedirectionMode::overwrite;
|
||||
if result.mode != RedirectionMode::Append {
|
||||
result.mode = RedirectionMode::Overwrite;
|
||||
}
|
||||
// Note 'echo abc >>? file' is valid: it means append and noclobber.
|
||||
// But here "noclobber" means the file must not exist, so appending
|
||||
// can be ignored.
|
||||
if try_consume(&mut cursor, '?') {
|
||||
result.mode = RedirectionMode::noclob;
|
||||
result.mode = RedirectionMode::NoClob;
|
||||
}
|
||||
}
|
||||
}
|
||||
'<' => {
|
||||
consume(&mut cursor, '<');
|
||||
if try_consume(&mut cursor, '&') {
|
||||
result.mode = RedirectionMode::fd;
|
||||
result.mode = RedirectionMode::Fd;
|
||||
} else if try_consume(&mut cursor, '?') {
|
||||
// <? foo try-input redirection (uses /dev/null if file can't be used).
|
||||
result.mode = RedirectionMode::try_input;
|
||||
result.mode = RedirectionMode::TryInput;
|
||||
} else {
|
||||
result.mode = RedirectionMode::input;
|
||||
result.mode = RedirectionMode::Input;
|
||||
}
|
||||
result.fd = if has_fd {
|
||||
parse_fd(fd_buff) // like 1<&3 or 1< /tmp/file.txt
|
||||
@@ -1111,12 +1111,12 @@ fn try_from(buff: &wstr) -> Result<PipeOrRedir, ()> {
|
||||
} else if try_consume(&mut cursor, '>') {
|
||||
result.fd = STDOUT_FILENO;
|
||||
result.stderr_merge = true;
|
||||
result.mode = RedirectionMode::overwrite;
|
||||
result.mode = RedirectionMode::Overwrite;
|
||||
if try_consume(&mut cursor, '>') {
|
||||
result.mode = RedirectionMode::append; // like &>>
|
||||
result.mode = RedirectionMode::Append; // like &>>
|
||||
}
|
||||
if try_consume(&mut cursor, '?') {
|
||||
result.mode = RedirectionMode::noclob; // like &>? or &>>?
|
||||
result.mode = RedirectionMode::NoClob; // like &>? or &>>?
|
||||
}
|
||||
} else {
|
||||
return Err(());
|
||||
@@ -1152,9 +1152,9 @@ pub fn is_valid(&self) -> bool {
|
||||
// Return the token type for this redirection.
|
||||
pub fn token_type(&self) -> TokenType {
|
||||
if self.is_pipe {
|
||||
TokenType::pipe
|
||||
TokenType::Pipe
|
||||
} else {
|
||||
TokenType::redirect
|
||||
TokenType::Redirect
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1418,14 +1418,14 @@ fn test_tokenizer() {
|
||||
let token = t.next(); // alpha
|
||||
assert!(token.is_some());
|
||||
let token = token.unwrap();
|
||||
assert_eq!(token.type_, TokenType::string);
|
||||
assert_eq!(token.type_, TokenType::String);
|
||||
assert_eq!(token.length, 5);
|
||||
assert_eq!(t.text_of(&token), "alpha");
|
||||
|
||||
let token = t.next(); // beta
|
||||
assert!(token.is_some());
|
||||
let token = token.unwrap();
|
||||
assert_eq!(token.type_, TokenType::string);
|
||||
assert_eq!(token.type_, TokenType::String);
|
||||
assert_eq!(token.offset, 6);
|
||||
assert_eq!(token.length, 4);
|
||||
assert_eq!(t.text_of(&token), "beta");
|
||||
@@ -1440,14 +1440,14 @@ fn test_tokenizer() {
|
||||
let token = t.next(); // {
|
||||
assert!(token.is_some());
|
||||
let token = token.unwrap();
|
||||
assert_eq!(token.type_, TokenType::left_brace);
|
||||
assert_eq!(token.type_, TokenType::LeftBrace);
|
||||
assert_eq!(token.length, 1);
|
||||
assert_eq!(t.text_of(&token), "{");
|
||||
|
||||
let token = t.next(); // echo
|
||||
assert!(token.is_some());
|
||||
let token = token.unwrap();
|
||||
assert_eq!(token.type_, TokenType::string);
|
||||
assert_eq!(token.type_, TokenType::String);
|
||||
assert_eq!(token.offset, 2);
|
||||
assert_eq!(token.length, 4);
|
||||
assert_eq!(t.text_of(&token), "echo");
|
||||
@@ -1459,26 +1459,26 @@ fn test_tokenizer() {
|
||||
let s = L!("{echo, foo}");
|
||||
let mut t = Tokenizer::new(s, TokFlags(0));
|
||||
let token = t.next().unwrap();
|
||||
assert_eq!(token.type_, TokenType::left_brace);
|
||||
assert_eq!(token.type_, TokenType::LeftBrace);
|
||||
assert_eq!(token.length, 1);
|
||||
}
|
||||
{
|
||||
let s = L!("{ echo; foo}");
|
||||
let mut t = Tokenizer::new(s, TokFlags(0));
|
||||
let token = t.next().unwrap();
|
||||
assert_eq!(token.type_, TokenType::left_brace);
|
||||
assert_eq!(token.type_, TokenType::LeftBrace);
|
||||
}
|
||||
|
||||
{
|
||||
let s = L!("{ | { name } '");
|
||||
let mut t = Tokenizer::new(s, TokFlags(0));
|
||||
let mut next_type = || t.next().unwrap().type_;
|
||||
assert_eq!(next_type(), TokenType::left_brace);
|
||||
assert_eq!(next_type(), TokenType::pipe);
|
||||
assert_eq!(next_type(), TokenType::left_brace);
|
||||
assert_eq!(next_type(), TokenType::string);
|
||||
assert_eq!(next_type(), TokenType::right_brace);
|
||||
assert_eq!(next_type(), TokenType::error);
|
||||
assert_eq!(next_type(), TokenType::LeftBrace);
|
||||
assert_eq!(next_type(), TokenType::Pipe);
|
||||
assert_eq!(next_type(), TokenType::LeftBrace);
|
||||
assert_eq!(next_type(), TokenType::String);
|
||||
assert_eq!(next_type(), TokenType::RightBrace);
|
||||
assert_eq!(next_type(), TokenType::Error);
|
||||
assert!(t.next().is_none());
|
||||
}
|
||||
|
||||
@@ -1493,10 +1493,10 @@ fn test_tokenizer() {
|
||||
type tt = TokenType;
|
||||
#[rustfmt::skip]
|
||||
let types = [
|
||||
tt::string, tt::redirect, tt::string, tt::redirect, tt::string, tt::string, tt::string,
|
||||
tt::string, tt::string, tt::pipe, tt::redirect, tt::andand, tt::background, tt::oror,
|
||||
tt::pipe, tt::andand, tt::oror, tt::background, tt::pipe, tt::string, tt::end,
|
||||
tt::string,
|
||||
tt::String, tt::Redirect, tt::String, tt::Redirect, tt::String, tt::String, tt::String,
|
||||
tt::String, tt::String, tt::Pipe, tt::Redirect, tt::AndAnd, tt::Background, tt::OrOr,
|
||||
tt::Pipe, tt::AndAnd, tt::OrOr, tt::Background, tt::Pipe, tt::String, tt::End,
|
||||
tt::String,
|
||||
];
|
||||
|
||||
{
|
||||
@@ -1513,8 +1513,8 @@ fn test_tokenizer() {
|
||||
{
|
||||
let mut t = Tokenizer::new(L!("abc\\"), TokFlags(0));
|
||||
let token = t.next().unwrap();
|
||||
assert_eq!(token.type_, TokenType::error);
|
||||
assert_eq!(token.error, TokenizerError::unterminated_escape);
|
||||
assert_eq!(token.type_, TokenType::Error);
|
||||
assert_eq!(token.error, TokenizerError::UnterminatedEscape);
|
||||
assert_eq!(token.error_offset_within_token, 3);
|
||||
}
|
||||
|
||||
@@ -1522,8 +1522,8 @@ fn test_tokenizer() {
|
||||
let mut t = Tokenizer::new(L!("abc )defg(hij"), TokFlags(0));
|
||||
let _token = t.next().unwrap();
|
||||
let token = t.next().unwrap();
|
||||
assert_eq!(token.type_, TokenType::error);
|
||||
assert_eq!(token.error, TokenizerError::closing_unopened_subshell);
|
||||
assert_eq!(token.type_, TokenType::Error);
|
||||
assert_eq!(token.error, TokenizerError::ClosingUnopenedSubshell);
|
||||
assert_eq!(token.offset, 4);
|
||||
assert_eq!(token.error_offset_within_token, 0);
|
||||
}
|
||||
@@ -1532,8 +1532,8 @@ fn test_tokenizer() {
|
||||
let mut t = Tokenizer::new(L!("abc defg(hij (klm)"), TokFlags(0));
|
||||
let _token = t.next().unwrap();
|
||||
let token = t.next().unwrap();
|
||||
assert_eq!(token.type_, TokenType::error);
|
||||
assert_eq!(token.error, TokenizerError::unterminated_subshell);
|
||||
assert_eq!(token.type_, TokenType::Error);
|
||||
assert_eq!(token.error, TokenizerError::UnterminatedSubshell);
|
||||
assert_eq!(token.error_offset_within_token, 4);
|
||||
}
|
||||
|
||||
@@ -1541,8 +1541,8 @@ fn test_tokenizer() {
|
||||
let mut t = Tokenizer::new(L!("abc defg[hij (klm)"), TokFlags(0));
|
||||
let _token = t.next().unwrap();
|
||||
let token = t.next().unwrap();
|
||||
assert_eq!(token.type_, TokenType::error);
|
||||
assert_eq!(token.error, TokenizerError::unterminated_slice);
|
||||
assert_eq!(token.type_, TokenType::Error);
|
||||
assert_eq!(token.error, TokenizerError::UnterminatedSlice);
|
||||
assert_eq!(token.error_offset_within_token, 4);
|
||||
}
|
||||
|
||||
@@ -1581,19 +1581,19 @@ macro_rules! get_redir_mode {
|
||||
};
|
||||
}
|
||||
|
||||
assert_eq!(get_redir_mode!("<"), RedirectionMode::input);
|
||||
assert_eq!(get_redir_mode!(">"), RedirectionMode::overwrite);
|
||||
assert_eq!(get_redir_mode!("2>"), RedirectionMode::overwrite);
|
||||
assert_eq!(get_redir_mode!(">>"), RedirectionMode::append);
|
||||
assert_eq!(get_redir_mode!("2>>"), RedirectionMode::append);
|
||||
assert_eq!(get_redir_mode!("2>?"), RedirectionMode::noclob);
|
||||
assert_eq!(get_redir_mode!("<"), RedirectionMode::Input);
|
||||
assert_eq!(get_redir_mode!(">"), RedirectionMode::Overwrite);
|
||||
assert_eq!(get_redir_mode!("2>"), RedirectionMode::Overwrite);
|
||||
assert_eq!(get_redir_mode!(">>"), RedirectionMode::Append);
|
||||
assert_eq!(get_redir_mode!("2>>"), RedirectionMode::Append);
|
||||
assert_eq!(get_redir_mode!("2>?"), RedirectionMode::NoClob);
|
||||
assert_eq!(
|
||||
get_redir_mode!("9999999999999999>?"),
|
||||
RedirectionMode::noclob
|
||||
RedirectionMode::NoClob
|
||||
);
|
||||
assert_eq!(get_redir_mode!("2>&3"), RedirectionMode::fd);
|
||||
assert_eq!(get_redir_mode!("3<&0"), RedirectionMode::fd);
|
||||
assert_eq!(get_redir_mode!("3</tmp/filetxt"), RedirectionMode::input);
|
||||
assert_eq!(get_redir_mode!("2>&3"), RedirectionMode::Fd);
|
||||
assert_eq!(get_redir_mode!("3<&0"), RedirectionMode::Fd);
|
||||
assert_eq!(get_redir_mode!("3</tmp/filetxt"), RedirectionMode::Input);
|
||||
}
|
||||
|
||||
/// Test word motion (forward-word, etc.). Carets represent cursor stops.
|
||||
|
||||
@@ -38,9 +38,9 @@
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Topic {
|
||||
sighupint = 0, // Corresponds to both SIGHUP and SIGINT signals.
|
||||
sigchld = 1, // Corresponds to SIGCHLD signal.
|
||||
internal_exit = 2, // Corresponds to an internal process exit.
|
||||
SigHupInt = 0, // Corresponds to both SIGHUP and SIGINT signals.
|
||||
SigChld = 1, // Corresponds to SIGCHLD signal.
|
||||
InternalExit = 2, // Corresponds to an internal process exit.
|
||||
}
|
||||
|
||||
// XXX: Is it correct to use the default or should the default be invalid_generation?
|
||||
@@ -70,7 +70,7 @@ impl FloggableDebug for Topic {}
|
||||
pub const INVALID_GENERATION: Generation = u64::MAX;
|
||||
|
||||
pub fn all_topics() -> [Topic; 3] {
|
||||
[Topic::sighupint, Topic::sigchld, Topic::internal_exit]
|
||||
[Topic::SigHupInt, Topic::SigChld, Topic::InternalExit]
|
||||
}
|
||||
|
||||
impl GenerationsList {
|
||||
@@ -106,18 +106,18 @@ fn describe(&self) -> WString {
|
||||
/// Sets the generation for `topic` to `value`.
|
||||
pub fn set(&self, topic: Topic, value: Generation) {
|
||||
match topic {
|
||||
Topic::sighupint => self.sighupint.set(value),
|
||||
Topic::sigchld => self.sigchld.set(value),
|
||||
Topic::internal_exit => self.internal_exit.set(value),
|
||||
Topic::SigHupInt => self.sighupint.set(value),
|
||||
Topic::SigChld => self.sigchld.set(value),
|
||||
Topic::InternalExit => self.internal_exit.set(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the value for a topic.
|
||||
pub fn get(&self, topic: Topic) -> Generation {
|
||||
match topic {
|
||||
Topic::sighupint => self.sighupint.get(),
|
||||
Topic::sigchld => self.sigchld.get(),
|
||||
Topic::internal_exit => self.internal_exit.get(),
|
||||
Topic::SigHupInt => self.sighupint.get(),
|
||||
Topic::SigChld => self.sigchld.get(),
|
||||
Topic::InternalExit => self.internal_exit.get(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,7 +621,7 @@ fn test_topic_monitor() {
|
||||
let _cleanup = test_init();
|
||||
let monitor = TopicMonitor::default();
|
||||
let gens = GenerationsList::new();
|
||||
let t = Topic::sigchld;
|
||||
let t = Topic::SigChld;
|
||||
gens.sigchld.set(0);
|
||||
assert_eq!(monitor.generation_for_topic(t), 0);
|
||||
let changed = monitor.check(&gens, false /* wait */);
|
||||
@@ -647,8 +647,8 @@ fn test_topic_monitor_torture() {
|
||||
let _cleanup = test_init();
|
||||
let monitor = Arc::new(TopicMonitor::default());
|
||||
const THREAD_COUNT: usize = 64;
|
||||
let t1 = Topic::sigchld;
|
||||
let t2 = Topic::sighupint;
|
||||
let t1 = Topic::SigChld;
|
||||
let t2 = Topic::SigHupInt;
|
||||
let mut gens_list = vec![GenerationsList::invalid(); THREAD_COUNT];
|
||||
let post_count = Arc::new(AtomicU64::new(0));
|
||||
for r#gen in &mut gens_list {
|
||||
|
||||
@@ -382,7 +382,7 @@ fn wildcard_test_flags_then_complete(
|
||||
// regular file *excludes* broken links - we have no use for them as commands.
|
||||
let is_regular_file = entry
|
||||
.check_type()
|
||||
.map(|x| x == DirEntryType::reg)
|
||||
.map(|x| x == DirEntryType::Reg)
|
||||
.unwrap_or(false);
|
||||
let is_executable = Lazy::new(|| is_regular_file && waccess(filepath, X_OK) == 0);
|
||||
if executables_only && !*is_executable {
|
||||
@@ -1227,7 +1227,7 @@ pub fn wildcard_has_internal(s: impl AsRef<wstr>) -> bool {
|
||||
#[must_use]
|
||||
pub fn wildcard_has(s: impl AsRef<wstr>) -> bool {
|
||||
let s = s.as_ref();
|
||||
let qmark_is_wild = !feature_test(FeatureFlag::qmark_noglob);
|
||||
let qmark_is_wild = !feature_test(FeatureFlag::QuestionMarkNoGlob);
|
||||
// Fast check for * or ?; if none there is no wildcard.
|
||||
// Note some strings contain * but no wildcards, e.g. if they are quoted.
|
||||
if !s.contains('*') && (!qmark_is_wild || !s.contains('?')) {
|
||||
@@ -1254,12 +1254,12 @@ fn test_wildcards() {
|
||||
let wc = unescape_string(wc, UnescapeStringStyle::Script(UnescapeFlags::SPECIAL)).unwrap();
|
||||
assert!(!wildcard_has(&wc) && wildcard_has_internal(&wc));
|
||||
|
||||
scoped_test(FeatureFlag::qmark_noglob, false, || {
|
||||
scoped_test(FeatureFlag::QuestionMarkNoGlob, false, || {
|
||||
assert!(wildcard_has(L!("?")));
|
||||
assert!(!wildcard_has(L!("\\?")));
|
||||
});
|
||||
|
||||
scoped_test(FeatureFlag::qmark_noglob, true, || {
|
||||
scoped_test(FeatureFlag::QuestionMarkNoGlob, true, || {
|
||||
assert!(!wildcard_has(L!("?")));
|
||||
assert!(!wildcard_has(L!("\\?")));
|
||||
});
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
/// Types of files that may be in a directory.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum DirEntryType {
|
||||
fifo = 1, // FIFO file
|
||||
chr, // character device
|
||||
dir, // directory
|
||||
blk, // block device
|
||||
reg, // regular file
|
||||
lnk, // symlink
|
||||
sock, // socket
|
||||
whiteout, // whiteout (from BSD)
|
||||
Fifo = 1, // FIFO file
|
||||
Chr, // character device
|
||||
Dir, // directory
|
||||
Blk, // block device
|
||||
Reg, // regular file
|
||||
Lnk, // symlink
|
||||
Sock, // socket
|
||||
Whiteout, // whiteout (from BSD)
|
||||
}
|
||||
|
||||
/// An entry returned by DirIter.
|
||||
@@ -72,7 +72,7 @@ pub fn check_type(&self) -> Option<DirEntryType> {
|
||||
|
||||
/// Return whether this is a directory. This may call stat().
|
||||
pub fn is_dir(&self) -> bool {
|
||||
self.check_type() == Some(DirEntryType::dir)
|
||||
self.check_type() == Some(DirEntryType::Dir)
|
||||
}
|
||||
|
||||
/// Return false if we know this can't be a link via d_type, true if it could be.
|
||||
@@ -119,7 +119,7 @@ fn do_stat(&self) {
|
||||
} else {
|
||||
match errno::errno().0 {
|
||||
ELOOP => {
|
||||
self.typ.set(Some(DirEntryType::lnk));
|
||||
self.typ.set(Some(DirEntryType::Lnk));
|
||||
}
|
||||
EACCES | EIO | ENOENT | ENOTDIR | ENAMETOOLONG | ENODEV => {
|
||||
// These are "expected" errors.
|
||||
@@ -140,13 +140,13 @@ fn do_stat(&self) {
|
||||
|
||||
fn dirent_type_to_entry_type(dt: u8) -> Option<DirEntryType> {
|
||||
match dt {
|
||||
DT_FIFO => Some(DirEntryType::fifo),
|
||||
DT_CHR => Some(DirEntryType::chr),
|
||||
DT_DIR => Some(DirEntryType::dir),
|
||||
DT_BLK => Some(DirEntryType::blk),
|
||||
DT_REG => Some(DirEntryType::reg),
|
||||
DT_LNK => Some(DirEntryType::lnk),
|
||||
DT_SOCK => Some(DirEntryType::sock),
|
||||
DT_FIFO => Some(DirEntryType::Fifo),
|
||||
DT_CHR => Some(DirEntryType::Chr),
|
||||
DT_DIR => Some(DirEntryType::Dir),
|
||||
DT_BLK => Some(DirEntryType::Blk),
|
||||
DT_REG => Some(DirEntryType::Reg),
|
||||
DT_LNK => Some(DirEntryType::Lnk),
|
||||
DT_SOCK => Some(DirEntryType::Sock),
|
||||
// todo!("whiteout")
|
||||
_ => None,
|
||||
}
|
||||
@@ -154,13 +154,13 @@ fn dirent_type_to_entry_type(dt: u8) -> Option<DirEntryType> {
|
||||
|
||||
fn stat_mode_to_entry_type(m: libc::mode_t) -> Option<DirEntryType> {
|
||||
match m & S_IFMT {
|
||||
S_IFIFO => Some(DirEntryType::fifo),
|
||||
S_IFCHR => Some(DirEntryType::chr),
|
||||
S_IFDIR => Some(DirEntryType::dir),
|
||||
S_IFBLK => Some(DirEntryType::blk),
|
||||
S_IFREG => Some(DirEntryType::reg),
|
||||
S_IFLNK => Some(DirEntryType::lnk),
|
||||
S_IFSOCK => Some(DirEntryType::sock),
|
||||
S_IFIFO => Some(DirEntryType::Fifo),
|
||||
S_IFCHR => Some(DirEntryType::Chr),
|
||||
S_IFDIR => Some(DirEntryType::Dir),
|
||||
S_IFBLK => Some(DirEntryType::Blk),
|
||||
S_IFREG => Some(DirEntryType::Reg),
|
||||
S_IFLNK => Some(DirEntryType::Lnk),
|
||||
S_IFSOCK => Some(DirEntryType::Sock),
|
||||
_ => {
|
||||
// todo!("whiteout")
|
||||
None
|
||||
@@ -294,11 +294,11 @@ pub fn next(&mut self) -> Option<io::Result<&DirEntry>> {
|
||||
);
|
||||
let typ = dirent_type_to_entry_type(dent.d_type);
|
||||
// Do not store symlinks as we will need to resolve them.
|
||||
if typ != Some(DirEntryType::lnk) {
|
||||
if typ != Some(DirEntryType::Lnk) {
|
||||
self.entry.typ.set(typ);
|
||||
}
|
||||
// This entry could be a link if it is a link or unknown.
|
||||
self.entry.possible_link = typ.map(|t| t == DirEntryType::lnk);
|
||||
self.entry.possible_link = typ.map(|t| t == DirEntryType::Lnk);
|
||||
|
||||
Some(Ok(&self.entry))
|
||||
}
|
||||
@@ -441,19 +441,19 @@ fn test_dir_iter() {
|
||||
assert!(names.iter().any(|&n| entry.name == n));
|
||||
|
||||
let expected = if entry.name == dirname {
|
||||
Some(DirEntryType::dir)
|
||||
Some(DirEntryType::Dir)
|
||||
} else if entry.name == regname {
|
||||
Some(DirEntryType::reg)
|
||||
Some(DirEntryType::Reg)
|
||||
} else if entry.name == reglinkname {
|
||||
Some(DirEntryType::reg)
|
||||
Some(DirEntryType::Reg)
|
||||
} else if entry.name == dirlinkname {
|
||||
Some(DirEntryType::dir)
|
||||
Some(DirEntryType::Dir)
|
||||
} else if entry.name == badlinkname {
|
||||
None
|
||||
} else if entry.name == selflinkname {
|
||||
Some(DirEntryType::lnk)
|
||||
Some(DirEntryType::Lnk)
|
||||
} else if entry.name == fifoname {
|
||||
Some(DirEntryType::fifo)
|
||||
Some(DirEntryType::Fifo)
|
||||
} else {
|
||||
panic!("Unexpected file type");
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user