Remove widestring-suffix uses

This removes both the `#[widestrs]` annotation as well as all `"foo"L`
suffixes, and does a `cargo fmt` run on the result
This commit is contained in:
Fabian Boehm 2024-01-12 19:10:56 +01:00
parent ca972f6e0f
commit 09cd7c7ad9
31 changed files with 567 additions and 614 deletions

View File

@ -2224,47 +2224,46 @@ impl BlockStatementHeaderVariant {
}
/// \return a string literal name for an ast type.
#[widestrs]
pub fn ast_type_to_string(t: Type) -> &'static wstr {
match t {
Type::token_base => "token_base"L,
Type::keyword_base => "keyword_base"L,
Type::redirection => "redirection"L,
Type::variable_assignment => "variable_assignment"L,
Type::variable_assignment_list => "variable_assignment_list"L,
Type::argument_or_redirection => "argument_or_redirection"L,
Type::argument_or_redirection_list => "argument_or_redirection_list"L,
Type::statement => "statement"L,
Type::job_pipeline => "job_pipeline"L,
Type::job_conjunction => "job_conjunction"L,
Type::for_header => "for_header"L,
Type::while_header => "while_header"L,
Type::function_header => "function_header"L,
Type::begin_header => "begin_header"L,
Type::block_statement => "block_statement"L,
Type::if_clause => "if_clause"L,
Type::elseif_clause => "elseif_clause"L,
Type::elseif_clause_list => "elseif_clause_list"L,
Type::else_clause => "else_clause"L,
Type::if_statement => "if_statement"L,
Type::case_item => "case_item"L,
Type::switch_statement => "switch_statement"L,
Type::decorated_statement => "decorated_statement"L,
Type::not_statement => "not_statement"L,
Type::job_continuation => "job_continuation"L,
Type::job_continuation_list => "job_continuation_list"L,
Type::job_conjunction_continuation => "job_conjunction_continuation"L,
Type::andor_job => "andor_job"L,
Type::andor_job_list => "andor_job_list"L,
Type::freestanding_argument_list => "freestanding_argument_list"L,
Type::token_conjunction => "token_conjunction"L,
Type::job_conjunction_continuation_list => "job_conjunction_continuation_list"L,
Type::maybe_newlines => "maybe_newlines"L,
Type::token_pipe => "token_pipe"L,
Type::case_item_list => "case_item_list"L,
Type::argument => "argument"L,
Type::argument_list => "argument_list"L,
Type::job_list => "job_list"L,
Type::token_base => L!("token_base"),
Type::keyword_base => L!("keyword_base"),
Type::redirection => L!("redirection"),
Type::variable_assignment => L!("variable_assignment"),
Type::variable_assignment_list => L!("variable_assignment_list"),
Type::argument_or_redirection => L!("argument_or_redirection"),
Type::argument_or_redirection_list => L!("argument_or_redirection_list"),
Type::statement => L!("statement"),
Type::job_pipeline => L!("job_pipeline"),
Type::job_conjunction => L!("job_conjunction"),
Type::for_header => L!("for_header"),
Type::while_header => L!("while_header"),
Type::function_header => L!("function_header"),
Type::begin_header => L!("begin_header"),
Type::block_statement => L!("block_statement"),
Type::if_clause => L!("if_clause"),
Type::elseif_clause => L!("elseif_clause"),
Type::elseif_clause_list => L!("elseif_clause_list"),
Type::else_clause => L!("else_clause"),
Type::if_statement => L!("if_statement"),
Type::case_item => L!("case_item"),
Type::switch_statement => L!("switch_statement"),
Type::decorated_statement => L!("decorated_statement"),
Type::not_statement => L!("not_statement"),
Type::job_continuation => L!("job_continuation"),
Type::job_continuation_list => L!("job_continuation_list"),
Type::job_conjunction_continuation => L!("job_conjunction_continuation"),
Type::andor_job => L!("andor_job"),
Type::andor_job_list => L!("andor_job_list"),
Type::freestanding_argument_list => L!("freestanding_argument_list"),
Type::token_conjunction => L!("token_conjunction"),
Type::job_conjunction_continuation_list => L!("job_conjunction_continuation_list"),
Type::maybe_newlines => L!("maybe_newlines"),
Type::token_pipe => L!("token_pipe"),
Type::case_item_list => L!("case_item_list"),
Type::argument => L!("argument"),
Type::argument_list => L!("argument_list"),
Type::job_list => L!("job_list"),
}
}
@ -2794,7 +2793,6 @@ impl<'s> NodeVisitorMut for Populator<'s> {
self.depth += 1
}
#[widestrs]
fn did_visit_fields_of<'a>(&'a mut self, node: &'a dyn NodeMut, flow: VisitResult) {
self.depth -= 1;
@ -2817,27 +2815,27 @@ impl<'s> NodeVisitorMut for Populator<'s> {
}
Type::for_header => {
let n = cursor.as_for_header().unwrap();
break Some((n.kw_for.range.unwrap(), "for loop"L));
break Some((n.kw_for.range.unwrap(), L!("for loop")));
}
Type::while_header => {
let n = cursor.as_while_header().unwrap();
break Some((n.kw_while.range.unwrap(), "while loop"L));
break Some((n.kw_while.range.unwrap(), L!("while loop")));
}
Type::function_header => {
let n = cursor.as_function_header().unwrap();
break Some((n.kw_function.range.unwrap(), "function definition"L));
break Some((n.kw_function.range.unwrap(), L!("function definition")));
}
Type::begin_header => {
let n = cursor.as_begin_header().unwrap();
break Some((n.kw_begin.range.unwrap(), "begin"L));
break Some((n.kw_begin.range.unwrap(), L!("begin")));
}
Type::if_statement => {
let n = cursor.as_if_statement().unwrap();
break Some((n.if_clause.kw_if.range.unwrap(), "if statement"L));
break Some((n.if_clause.kw_if.range.unwrap(), L!("if statement")));
}
Type::switch_statement => {
let n = cursor.as_switch_statement().unwrap();
break Some((n.kw_switch.range.unwrap(), "switch statement"L));
break Some((n.kw_switch.range.unwrap(), L!("switch statement")));
}
_ => break None,
}
@ -2919,31 +2917,29 @@ impl<'s> NodeVisitorMut for Populator<'s> {
/// Helper to describe a list of keywords.
/// TODO: these need to be localized properly.
#[widestrs]
fn keywords_user_presentable_description(kws: &'static [ParseKeyword]) -> WString {
assert!(!kws.is_empty(), "Should not be empty list");
if kws.len() == 1 {
return sprintf!("keyword '%ls'"L, kws[0]);
return sprintf!(L!("keyword '%ls'"), kws[0]);
}
let mut res = "keywords "L.to_owned();
let mut res = L!("keywords ").to_owned();
for (i, kw) in kws.iter().enumerate() {
if i != 0 {
res += " or "L;
res += L!(" or ");
}
res += &sprintf!("'%ls'"L, *kw)[..];
res += &sprintf!(L!("'%ls'"), *kw)[..];
}
res
}
/// Helper to describe a list of token types.
/// TODO: these need to be localized properly.
#[widestrs]
fn token_types_user_presentable_description(types: &'static [ParseTokenType]) -> WString {
assert!(!types.is_empty(), "Should not be empty list");
let mut res = WString::new();
for typ in types {
if !res.is_empty() {
res += " or "L;
res += L!(" or ");
}
res += &token_type_user_presentable_description(*typ, ParseKeyword::none)[..];
}

View File

@ -318,7 +318,6 @@ impl AutoloadFileCache {
}
}
#[widestring_suffix::widestrs]
#[test]
#[serial]
fn test_autoload() {
@ -349,47 +348,53 @@ fn test_autoload() {
let p2 = charptr2wcstring(unsafe { libc::mkdtemp(t2.as_mut_ptr().cast()) });
let paths = &[p1.clone(), p2.clone()];
let mut autoload = Autoload::new("test_var"L);
assert!(autoload.resolve_command_impl("file1"L, paths).is_none());
assert!(autoload.resolve_command_impl("nothing"L, paths).is_none());
let mut autoload = Autoload::new(L!("test_var"));
assert!(autoload.resolve_command_impl(L!("file1"), paths).is_none());
assert!(autoload
.resolve_command_impl(L!("nothing"), paths)
.is_none());
assert!(autoload.get_autoloaded_commands().is_empty());
run!("touch %ls/file1.fish", p1);
run!("touch %ls/file2.fish", p2);
autoload.invalidate_cache();
assert!(!autoload.autoload_in_progress("file1"L));
assert!(autoload.resolve_command_impl("file1"L, paths).is_some());
assert!(autoload.resolve_command_impl("file1"L, paths).is_none());
assert!(autoload.autoload_in_progress("file1"L));
assert!(autoload.get_autoloaded_commands() == vec!["file1"L]);
autoload.mark_autoload_finished("file1"L);
assert!(!autoload.autoload_in_progress("file1"L));
assert!(autoload.get_autoloaded_commands() == vec!["file1"L]);
assert!(!autoload.autoload_in_progress(L!("file1")));
assert!(autoload.resolve_command_impl(L!("file1"), paths).is_some());
assert!(autoload.resolve_command_impl(L!("file1"), paths).is_none());
assert!(autoload.autoload_in_progress(L!("file1")));
assert!(autoload.get_autoloaded_commands() == vec![L!("file1")]);
autoload.mark_autoload_finished(L!("file1"));
assert!(!autoload.autoload_in_progress(L!("file1")));
assert!(autoload.get_autoloaded_commands() == vec![L!("file1")]);
assert!(autoload.resolve_command_impl("file1"L, paths).is_none());
assert!(autoload.resolve_command_impl("nothing"L, paths).is_none());
assert!(autoload.resolve_command_impl("file2"L, paths).is_some());
assert!(autoload.resolve_command_impl("file2"L, paths).is_none());
autoload.mark_autoload_finished("file2"L);
assert!(autoload.resolve_command_impl("file2"L, paths).is_none());
assert!((autoload.get_autoloaded_commands() == vec!["file1"L, "file2"L]));
assert!(autoload.resolve_command_impl(L!("file1"), paths).is_none());
assert!(autoload
.resolve_command_impl(L!("nothing"), paths)
.is_none());
assert!(autoload.resolve_command_impl(L!("file2"), paths).is_some());
assert!(autoload.resolve_command_impl(L!("file2"), paths).is_none());
autoload.mark_autoload_finished(L!("file2"));
assert!(autoload.resolve_command_impl(L!("file2"), paths).is_none());
assert!((autoload.get_autoloaded_commands() == vec![L!("file1"), L!("file2")]));
autoload.clear();
assert!(autoload.resolve_command_impl("file1"L, paths).is_some());
autoload.mark_autoload_finished("file1"L);
assert!(autoload.resolve_command_impl("file1"L, paths).is_none());
assert!(autoload.resolve_command_impl("nothing"L, paths).is_none());
assert!(autoload.resolve_command_impl("file2"L, paths).is_some());
assert!(autoload.resolve_command_impl("file2"L, paths).is_none());
autoload.mark_autoload_finished("file2"L);
assert!(autoload.resolve_command_impl(L!("file1"), paths).is_some());
autoload.mark_autoload_finished(L!("file1"));
assert!(autoload.resolve_command_impl(L!("file1"), paths).is_none());
assert!(autoload
.resolve_command_impl(L!("nothing"), paths)
.is_none());
assert!(autoload.resolve_command_impl(L!("file2"), paths).is_some());
assert!(autoload.resolve_command_impl(L!("file2"), paths).is_none());
autoload.mark_autoload_finished(L!("file2"));
assert!(autoload.resolve_command_impl("file1"L, paths).is_none());
assert!(autoload.resolve_command_impl(L!("file1"), paths).is_none());
touch_file(&sprintf!("%ls/file1.fish", p1));
autoload.invalidate_cache();
assert!(autoload.resolve_command_impl("file1"L, paths).is_some());
autoload.mark_autoload_finished("file1"L);
assert!(autoload.resolve_command_impl(L!("file1"), paths).is_some());
autoload.mark_autoload_finished(L!("file1"));
run!("rm -Rf %ls"L, p1);
run!("rm -Rf %ls"L, p2);
run!(L!("rm -Rf %ls"), p1);
run!(L!("rm -Rf %ls"), p2);
}

View File

@ -1,7 +1,6 @@
use super::prelude::*;
use crate::event;
#[widestrs]
pub fn emit(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Option<c_int> {
let cmd = argv[0];
@ -19,7 +18,7 @@ pub fn emit(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Opt
let Some(event_name) = argv.get(opts.optind) else {
streams
.err
.append(sprintf!("%ls: expected event name\n"L, cmd));
.append(sprintf!(L!("%ls: expected event name\n"), cmd));
return STATUS_INVALID_ARGS;
};

View File

@ -14,22 +14,21 @@ struct Options {
base: usize,
}
#[widestrs]
fn parse_cmd_opts(
args: &mut [&wstr],
parser: &Parser,
streams: &mut IoStreams,
) -> Result<(Options, usize), Option<c_int>> {
const cmd: &wstr = "math"L;
const cmd: &wstr = L!("math");
let print_hints = true;
// This command is atypical in using the "+" (REQUIRE_ORDER) option for flag parsing.
// This is needed because of the minus, `-`, operator in math expressions.
const SHORT_OPTS: &wstr = "+:hs:b:"L;
const SHORT_OPTS: &wstr = L!("+:hs:b:");
const LONG_OPTS: &[woption] = &[
wopt("scale"L, woption_argument_t::required_argument, 's'),
wopt("base"L, woption_argument_t::required_argument, 'b'),
wopt("help"L, woption_argument_t::no_argument, 'h'),
wopt(L!("scale"), woption_argument_t::required_argument, 's'),
wopt(L!("base"), woption_argument_t::required_argument, 'b'),
wopt(L!("help"), woption_argument_t::no_argument, 'h'),
];
let mut opts = Options {
@ -158,7 +157,6 @@ fn format_double(mut v: f64, opts: &Options) -> WString {
ret
}
#[widestrs]
fn evaluate_expression(
cmd: &wstr,
streams: &mut IoStreams,
@ -174,11 +172,11 @@ fn evaluate_expression(
// (e.g. infinite is the result of "x / 0"),
// but that's much more work.
let error_message = if n.is_infinite() {
"Result is infinite"L
L!("Result is infinite")
} else if n.is_nan() {
"Result is not a number"L
L!("Result is not a number")
} else if n.abs() >= MAX_CONTIGUOUS_INTEGER {
"Result magnitude is too large"L
L!("Result magnitude is too large")
} else {
let mut s = format_double(n, opts);
s.push('\n');
@ -189,24 +187,26 @@ fn evaluate_expression(
streams
.err
.append(sprintf!("%ls: Error: %ls\n"L, cmd, error_message));
streams.err.append(sprintf!("'%ls'\n"L, expression));
.append(sprintf!(L!("%ls: Error: %ls\n"), cmd, error_message));
streams.err.append(sprintf!(L!("'%ls'\n"), expression));
STATUS_CMD_ERROR
}
Err(err) => {
streams.err.append(sprintf!(
"%ls: Error: %ls\n"L,
L!("%ls: Error: %ls\n"),
cmd,
err.kind.describe_wstr()
));
streams.err.append(sprintf!("'%ls'\n"L, expression));
streams.err.append(sprintf!(L!("'%ls'\n"), expression));
let padding = WString::from_chars(vec![' '; err.position + 1]);
if err.len >= 2 {
let tildes = WString::from_chars(vec!['~'; err.len - 2]);
streams.err.append(sprintf!("%ls^%ls^\n"L, padding, tildes));
streams
.err
.append(sprintf!(L!("%ls^%ls^\n"), padding, tildes));
} else {
streams.err.append(sprintf!("%ls^\n"L, padding));
streams.err.append(sprintf!(L!("%ls^\n"), padding));
}
STATUS_CMD_ERROR
@ -218,7 +218,6 @@ fn evaluate_expression(
const MATH_CHUNK_SIZE: usize = 1024;
/// The math builtin evaluates math expressions.
#[widestrs]
pub fn math(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Option<c_int> {
let cmd = argv[0];

View File

@ -17,7 +17,6 @@ use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::os::fd::FromRawFd;
use std::sync::Arc;
use widestring_suffix::widestrs;
pub type BuiltinCmd = fn(&Parser, &mut IoStreams, &mut [&wstr]) -> Option<c_int>;
@ -115,250 +114,249 @@ struct BuiltinData {
// Data about all the builtin commands in fish.
// Functions that are bound to builtin_generic are handled directly by the parser.
// NOTE: These must be kept in sorted order!
#[widestrs]
const BUILTIN_DATAS: &[BuiltinData] = &[
BuiltinData {
name: "."L,
name: L!("."),
func: source::source,
},
BuiltinData {
name: ":"L,
name: L!(":"),
func: builtin_true,
},
BuiltinData {
name: "["L, // ]
name: L!("["), // ]
func: test::test,
},
BuiltinData {
name: "_"L,
name: L!("_"),
func: builtin_gettext,
},
BuiltinData {
name: "abbr"L,
name: L!("abbr"),
func: abbr::abbr,
},
BuiltinData {
name: "and"L,
name: L!("and"),
func: builtin_generic,
},
BuiltinData {
name: "argparse"L,
name: L!("argparse"),
func: argparse::argparse,
},
BuiltinData {
name: "begin"L,
name: L!("begin"),
func: builtin_generic,
},
BuiltinData {
name: "bg"L,
name: L!("bg"),
func: bg::bg,
},
BuiltinData {
name: "bind"L,
name: L!("bind"),
func: bind::bind,
},
BuiltinData {
name: "block"L,
name: L!("block"),
func: block::block,
},
BuiltinData {
name: "break"L,
name: L!("break"),
func: builtin_break_continue,
},
BuiltinData {
name: "breakpoint"L,
name: L!("breakpoint"),
func: builtin_breakpoint,
},
BuiltinData {
name: "builtin"L,
name: L!("builtin"),
func: builtin::builtin,
},
BuiltinData {
name: "case"L,
name: L!("case"),
func: builtin_generic,
},
BuiltinData {
name: "cd"L,
name: L!("cd"),
func: cd::cd,
},
BuiltinData {
name: "command"L,
name: L!("command"),
func: command::command,
},
BuiltinData {
name: "commandline"L,
name: L!("commandline"),
func: commandline::commandline,
},
BuiltinData {
name: "complete"L,
name: L!("complete"),
func: complete::complete,
},
BuiltinData {
name: "contains"L,
name: L!("contains"),
func: contains::contains,
},
BuiltinData {
name: "continue"L,
name: L!("continue"),
func: builtin_break_continue,
},
BuiltinData {
name: "count"L,
name: L!("count"),
func: count::count,
},
BuiltinData {
name: "disown"L,
name: L!("disown"),
func: disown::disown,
},
BuiltinData {
name: "echo"L,
name: L!("echo"),
func: echo::echo,
},
BuiltinData {
name: "else"L,
name: L!("else"),
func: builtin_generic,
},
BuiltinData {
name: "emit"L,
name: L!("emit"),
func: emit::emit,
},
BuiltinData {
name: "end"L,
name: L!("end"),
func: builtin_generic,
},
BuiltinData {
name: "eval"L,
name: L!("eval"),
func: eval::eval,
},
BuiltinData {
name: "exec"L,
name: L!("exec"),
func: builtin_generic,
},
BuiltinData {
name: "exit"L,
name: L!("exit"),
func: exit::exit,
},
BuiltinData {
name: "false"L,
name: L!("false"),
func: builtin_false,
},
BuiltinData {
name: "fg"L,
name: L!("fg"),
func: fg::fg,
},
BuiltinData {
name: "for"L,
name: L!("for"),
func: builtin_generic,
},
BuiltinData {
name: "function"L,
name: L!("function"),
func: builtin_generic,
},
BuiltinData {
name: "functions"L,
name: L!("functions"),
func: functions::functions,
},
BuiltinData {
name: "history"L,
name: L!("history"),
func: history::history,
},
BuiltinData {
name: "if"L,
name: L!("if"),
func: builtin_generic,
},
BuiltinData {
name: "jobs"L,
name: L!("jobs"),
func: jobs::jobs,
},
BuiltinData {
name: "math"L,
name: L!("math"),
func: math::math,
},
BuiltinData {
name: "not"L,
name: L!("not"),
func: builtin_generic,
},
BuiltinData {
name: "or"L,
name: L!("or"),
func: builtin_generic,
},
BuiltinData {
name: "path"L,
name: L!("path"),
func: path::path,
},
BuiltinData {
name: "printf"L,
name: L!("printf"),
func: printf::printf,
},
BuiltinData {
name: "pwd"L,
name: L!("pwd"),
func: pwd::pwd,
},
BuiltinData {
name: "random"L,
name: L!("random"),
func: random::random,
},
BuiltinData {
name: "read"L,
name: L!("read"),
func: read::read,
},
BuiltinData {
name: "realpath"L,
name: L!("realpath"),
func: realpath::realpath,
},
BuiltinData {
name: "return"L,
name: L!("return"),
func: r#return::r#return,
},
BuiltinData {
name: "set"L,
name: L!("set"),
func: set::set,
},
BuiltinData {
name: "set_color"L,
name: L!("set_color"),
func: set_color::set_color,
},
BuiltinData {
name: "source"L,
name: L!("source"),
func: source::source,
},
BuiltinData {
name: "status"L,
name: L!("status"),
func: status::status,
},
BuiltinData {
name: "string"L,
name: L!("string"),
func: string::string,
},
BuiltinData {
name: "switch"L,
name: L!("switch"),
func: builtin_generic,
},
BuiltinData {
name: "test"L,
name: L!("test"),
func: test::test,
},
BuiltinData {
name: "time"L,
name: L!("time"),
func: builtin_generic,
},
BuiltinData {
name: "true"L,
name: L!("true"),
func: builtin_true,
},
BuiltinData {
name: "type"L,
name: L!("type"),
func: r#type::r#type,
},
BuiltinData {
name: "ulimit"L,
name: L!("ulimit"),
func: ulimit::ulimit,
},
BuiltinData {
name: "wait"L,
name: L!("wait"),
func: wait::wait,
},
BuiltinData {
name: "while"L,
name: L!("while"),
func: builtin_generic,
},
];
@ -380,16 +378,15 @@ pub fn builtin_exists(name: &wstr) -> bool {
}
/// Is the command a keyword we need to special-case the handling of `-h` and `--help`.
#[widestrs]
fn cmd_needs_help(cmd: &wstr) -> bool {
[
"for"L,
"while"L,
"function"L,
"if"L,
"end"L,
"switch"L,
"case"L,
L!("for"),
L!("while"),
L!("function"),
L!("if"),
L!("end"),
L!("switch"),
L!("case"),
]
.contains(&cmd)
}

View File

@ -47,7 +47,6 @@ fn run_test_test(expected: i32, lst: &[&str]) -> bool {
nobracket
}
#[widestrs]
fn test_test_brackets() {
// Ensure [ knows it needs a ].
let parser = Parser::principal_parser();
@ -57,16 +56,16 @@ fn test_test_brackets() {
let io_chain = IoChain::new();
let mut streams = IoStreams::new(&mut out, &mut err, &io_chain);
let args1 = &mut ["["L, "foo"L];
let args1 = &mut [L!("["), L!("foo")];
assert_eq!(
builtin_test(parser, &mut streams, args1),
STATUS_INVALID_ARGS
);
let args2 = &mut ["["L, "foo"L, "]"L];
let args2 = &mut [L!("["), L!("foo"), L!("]")];
assert_eq!(builtin_test(parser, &mut streams, args2), STATUS_CMD_OK);
let args3 = &mut ["["L, "foo"L, "]"L, "bar"L];
let args3 = &mut [L!("["), L!("foo"), L!("]"), L!("bar")];
assert_eq!(
builtin_test(parser, &mut streams, args3),
STATUS_INVALID_ARGS

View File

@ -127,7 +127,6 @@ fn wait_for_completion(parser: &Parser, whs: &[WaitHandleRef], any_flag: bool) -
}
}
#[widestrs]
pub fn wait(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Option<c_int> {
let cmd = argv[0];
let argc = argv.len();
@ -135,10 +134,10 @@ pub fn wait(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Opt
let mut print_help = false;
let print_hints = false;
const shortopts: &wstr = ":nh"L;
const shortopts: &wstr = L!(":nh");
const longopts: &[woption] = &[
wopt("any"L, woption_argument_t::no_argument, 'n'),
wopt("help"L, woption_argument_t::no_argument, 'h'),
wopt(L!("any"), woption_argument_t::no_argument, 'n'),
wopt(L!("help"), woption_argument_t::no_argument, 'h'),
];
let mut w = wgetopter_t::new(shortopts, longopts, argv);

View File

@ -231,13 +231,12 @@ impl RgbColor {
}
/// Try parsing a special color name like "normal".
#[widestrs]
fn try_parse_special(special: &wstr) -> Option<Self> {
// TODO: this is a very hot function, may need optimization by e.g. comparing length first,
// depending on how well inlining of `simple_icase_compare` works
let typ = if simple_icase_compare(special, "normal"L) == Ordering::Equal {
let typ = if simple_icase_compare(special, L!("normal")) == Ordering::Equal {
Type::Normal
} else if simple_icase_compare(special, "reset"L) == Ordering::Equal {
} else if simple_icase_compare(special, L!("reset")) == Ordering::Equal {
Type::Reset
} else {
return None;
@ -320,32 +319,31 @@ struct NamedColor {
hidden: bool,
}
#[widestrs]
#[rustfmt::skip]
const NAMED_COLORS: &[NamedColor] = &[
// Keep this sorted alphabetically
NamedColor {name: "black"L, idx: 0, _rgb: [0x00, 0x00, 0x00], hidden: false},
NamedColor {name: "blue"L, idx: 4, _rgb: [0x00, 0x00, 0x80], hidden: false},
NamedColor {name: "brblack"L, idx: 8, _rgb: [0x80, 0x80, 0x80], hidden: false},
NamedColor {name: "brblue"L, idx: 12, _rgb: [0x00, 0x00, 0xFF], hidden: false},
NamedColor {name: "brbrown"L, idx: 11, _rgb: [0xFF, 0xFF, 0x00], hidden: true},
NamedColor {name: "brcyan"L, idx: 14, _rgb: [0x00, 0xFF, 0xFF], hidden: false},
NamedColor {name: "brgreen"L, idx: 10, _rgb: [0x00, 0xFF, 0x00], hidden: false},
NamedColor {name: "brgrey"L, idx: 8, _rgb: [0x55, 0x55, 0x55], hidden: true},
NamedColor {name: "brmagenta"L, idx: 13, _rgb: [0xFF, 0x00, 0xFF], hidden: false},
NamedColor {name: "brown"L, idx: 3, _rgb: [0x72, 0x50, 0x00], hidden: true},
NamedColor {name: "brpurple"L, idx: 13, _rgb: [0xFF, 0x00, 0xFF], hidden: true},
NamedColor {name: "brred"L, idx: 9, _rgb: [0xFF, 0x00, 0x00], hidden: false},
NamedColor {name: "brwhite"L, idx: 15, _rgb: [0xFF, 0xFF, 0xFF], hidden: false},
NamedColor {name: "bryellow"L, idx: 11, _rgb: [0xFF, 0xFF, 0x00], hidden: false},
NamedColor {name: "cyan"L, idx: 6, _rgb: [0x00, 0x80, 0x80], hidden: false},
NamedColor {name: "green"L, idx: 2, _rgb: [0x00, 0x80, 0x00], hidden: false},
NamedColor {name: "grey"L, idx: 7, _rgb: [0xE5, 0xE5, 0xE5], hidden: true},
NamedColor {name: "magenta"L, idx: 5, _rgb: [0x80, 0x00, 0x80], hidden: false},
NamedColor {name: "purple"L, idx: 5, _rgb: [0x80, 0x00, 0x80], hidden: true},
NamedColor {name: "red"L, idx: 1, _rgb: [0x80, 0x00, 0x00], hidden: false},
NamedColor {name: "white"L, idx: 7, _rgb: [0xC0, 0xC0, 0xC0], hidden: false},
NamedColor {name: "yellow"L, idx: 3, _rgb: [0x80, 0x80, 0x00], hidden: false},
NamedColor {name: L!("black"), idx: 0, _rgb: [0x00, 0x00, 0x00], hidden: false},
NamedColor {name: L!("blue"), idx: 4, _rgb: [0x00, 0x00, 0x80], hidden: false},
NamedColor {name: L!("brblack"), idx: 8, _rgb: [0x80, 0x80, 0x80], hidden: false},
NamedColor {name: L!("brblue"), idx: 12, _rgb: [0x00, 0x00, 0xFF], hidden: false},
NamedColor {name: L!("brbrown"), idx: 11, _rgb: [0xFF, 0xFF, 0x00], hidden: true},
NamedColor {name: L!("brcyan"), idx: 14, _rgb: [0x00, 0xFF, 0xFF], hidden: false},
NamedColor {name: L!("brgreen"), idx: 10, _rgb: [0x00, 0xFF, 0x00], hidden: false},
NamedColor {name: L!("brgrey"), idx: 8, _rgb: [0x55, 0x55, 0x55], hidden: true},
NamedColor {name: L!("brmagenta"), idx: 13, _rgb: [0xFF, 0x00, 0xFF], hidden: false},
NamedColor {name: L!("brown"), idx: 3, _rgb: [0x72, 0x50, 0x00], hidden: true},
NamedColor {name: L!("brpurple"), idx: 13, _rgb: [0xFF, 0x00, 0xFF], hidden: true},
NamedColor {name: L!("brred"), idx: 9, _rgb: [0xFF, 0x00, 0x00], hidden: false},
NamedColor {name: L!("brwhite"), idx: 15, _rgb: [0xFF, 0xFF, 0xFF], hidden: false},
NamedColor {name: L!("bryellow"), idx: 11, _rgb: [0xFF, 0xFF, 0x00], hidden: false},
NamedColor {name: L!("cyan"), idx: 6, _rgb: [0x00, 0x80, 0x80], hidden: false},
NamedColor {name: L!("green"), idx: 2, _rgb: [0x00, 0x80, 0x00], hidden: false},
NamedColor {name: L!("grey"), idx: 7, _rgb: [0xE5, 0xE5, 0xE5], hidden: true},
NamedColor {name: L!("magenta"), idx: 5, _rgb: [0x80, 0x00, 0x80], hidden: false},
NamedColor {name: L!("purple"), idx: 5, _rgb: [0x80, 0x00, 0x80], hidden: true},
NamedColor {name: L!("red"), idx: 1, _rgb: [0x80, 0x00, 0x00], hidden: false},
NamedColor {name: L!("white"), idx: 7, _rgb: [0xC0, 0xC0, 0xC0], hidden: false},
NamedColor {name: L!("yellow"), idx: 3, _rgb: [0x80, 0x80, 0x00], hidden: false},
];
assert_sorted_by_name!(NAMED_COLORS);
@ -434,28 +432,26 @@ mod tests {
use crate::wchar::prelude::*;
#[test]
#[widestrs]
fn parse() {
assert!(RgbColor::from_wstr("#FF00A0"L).unwrap().is_rgb());
assert!(RgbColor::from_wstr("FF00A0"L).unwrap().is_rgb());
assert!(RgbColor::from_wstr("#F30"L).unwrap().is_rgb());
assert!(RgbColor::from_wstr("F30"L).unwrap().is_rgb());
assert!(RgbColor::from_wstr("f30"L).unwrap().is_rgb());
assert!(RgbColor::from_wstr("#FF30a5"L).unwrap().is_rgb());
assert!(RgbColor::from_wstr("3f30"L).is_none());
assert!(RgbColor::from_wstr("##f30"L).is_none());
assert!(RgbColor::from_wstr("magenta"L).unwrap().is_named());
assert!(RgbColor::from_wstr("MaGeNTa"L).unwrap().is_named());
assert!(RgbColor::from_wstr("mooganta"L).is_none());
assert!(RgbColor::from_wstr(L!("#FF00A0")).unwrap().is_rgb());
assert!(RgbColor::from_wstr(L!("FF00A0")).unwrap().is_rgb());
assert!(RgbColor::from_wstr(L!("#F30")).unwrap().is_rgb());
assert!(RgbColor::from_wstr(L!("F30")).unwrap().is_rgb());
assert!(RgbColor::from_wstr(L!("f30")).unwrap().is_rgb());
assert!(RgbColor::from_wstr(L!("#FF30a5")).unwrap().is_rgb());
assert!(RgbColor::from_wstr(L!("3f30")).is_none());
assert!(RgbColor::from_wstr(L!("##f30")).is_none());
assert!(RgbColor::from_wstr(L!("magenta")).unwrap().is_named());
assert!(RgbColor::from_wstr(L!("MaGeNTa")).unwrap().is_named());
assert!(RgbColor::from_wstr(L!("mooganta")).is_none());
}
#[test]
#[widestrs]
fn parse_rgb() {
assert!(RgbColor::from_wstr("##FF00A0"L) == None);
assert!(RgbColor::from_wstr("#FF00A0"L) == Some(RgbColor::from_rgb(0xff, 0x00, 0xa0)));
assert!(RgbColor::from_wstr("FF00A0"L) == Some(RgbColor::from_rgb(0xff, 0x00, 0xa0)));
assert!(RgbColor::from_wstr("FAF"L) == Some(RgbColor::from_rgb(0xff, 0xaa, 0xff)));
assert!(RgbColor::from_wstr(L!("##FF00A0")) == None);
assert!(RgbColor::from_wstr(L!("#FF00A0")) == Some(RgbColor::from_rgb(0xff, 0x00, 0xa0)));
assert!(RgbColor::from_wstr(L!("FF00A0")) == Some(RgbColor::from_rgb(0xff, 0x00, 0xa0)));
assert!(RgbColor::from_wstr(L!("FAF")) == Some(RgbColor::from_rgb(0xff, 0xaa, 0xff)));
}
// Regression test for multiplicative overflow in convert_color.

View File

@ -180,7 +180,6 @@ pub fn escape_string(s: &wstr, style: EscapeStringStyle) -> WString {
}
/// Escape a string in a fashion suitable for using in fish script.
#[widestrs]
fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
let escape_printables = !flags.contains(EscapeFlags::NO_PRINTABLES);
let no_quoted = flags.contains(EscapeFlags::NO_QUOTED);
@ -197,7 +196,7 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
let mut need_complex_escape = false;
if !no_quoted && input.is_empty() {
return "''"L.to_owned();
return L!("''").to_owned();
}
let mut out = WString::new();
@ -220,7 +219,7 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
if symbolic {
out.push('␉');
} else {
out += "\\t"L;
out += L!("\\t");
}
need_escape = true;
need_complex_escape = true;
@ -229,7 +228,7 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
if symbolic {
out.push('␤');
} else {
out += "\\n"L;
out += L!("\\n");
}
need_escape = true;
need_complex_escape = true;
@ -238,7 +237,7 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
if symbolic {
out.push('␈');
} else {
out += "\\b"L;
out += L!("\\b");
}
need_escape = true;
need_complex_escape = true;
@ -247,7 +246,7 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
if symbolic {
out.push('␍');
} else {
out += "\\r"L;
out += L!("\\r");
}
need_escape = true;
need_complex_escape = true;
@ -256,7 +255,7 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
if symbolic {
out.push('␛');
} else {
out += "\\e"L;
out += L!("\\e");
}
need_escape = true;
need_complex_escape = true;
@ -265,7 +264,7 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
if symbolic {
out.push('␡');
} else {
out += "\\x7f"L;
out += L!("\\x7f");
}
need_escape = true;
need_complex_escape = true;
@ -286,7 +285,7 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
out.push('*');
}
ANY_STRING_RECURSIVE => {
out += "**"L;
out += L!("**");
}
'&' | '$' | ' ' | '#' | '<' | '>' | '(' | ')' | '[' | ']' | '{' | '}' | '?' | '*'
@ -358,7 +357,6 @@ fn byte_to_hex(byte: u8) -> (char, char) {
}
/// Escape a string in a fashion suitable for using as a URL. Store the result in out_str.
#[widestrs]
fn escape_string_url(input: &wstr) -> WString {
let narrow = wcs2string(input);
let mut out = WString::new();
@ -1246,8 +1244,7 @@ fn count_ascii_prefix(inp: &[u8]) -> usize {
}
// Check if we are running in the test mode, where we should suppress error output
#[widestrs]
pub const TESTS_PROGRAM_NAME: &wstr = "(ignore)"L;
pub const TESTS_PROGRAM_NAME: &wstr = L!("(ignore)");
/// Hack to not print error messages in the tests. Do not call this from functions in this module
/// like `debug()`. It is only intended to suppress diagnostic noise from testing things like the
@ -1533,7 +1530,6 @@ pub fn read_loop<Fd: AsRawFd>(fd: &Fd, buf: &mut [u8]) -> std::io::Result<usize>
}
/// Write the given paragraph of output, redoing linebreaks to fit \p termsize.
#[widestrs]
pub fn reformat_for_screen(msg: &wstr, termsize: &Termsize) -> WString {
let mut buff = WString::new();
@ -1568,7 +1564,7 @@ pub fn reformat_for_screen(msg: &wstr, termsize: &Termsize) -> WString {
if line_width != 0 {
buff.push('\n');
}
buff += &sprintf!("%ls-\n"L, token)[..];
buff += &sprintf!(L!("%ls-\n"), token)[..];
line_width = 0;
} else {
// Print the token.
@ -1579,7 +1575,7 @@ pub fn reformat_for_screen(msg: &wstr, termsize: &Termsize) -> WString {
line_width = 0;
}
if line_width != 0 {
buff += " "L;
buff += L!(" ");
}
buff += token;
line_width += line_width_unit + tok_width;

View File

@ -336,19 +336,18 @@ impl EnvScopedImpl {
self.perproc_data.statuses = s;
}
#[widestrs]
fn try_get_computed(&self, key: &wstr) -> Option<EnvVar> {
let ev = ElectricVar::for_name(key);
if ev.is_none() || !ev.unwrap().computed() {
return None;
}
if key == "PWD"L {
if key == L!("PWD") {
Some(EnvVar::new(
self.perproc_data.pwd.clone(),
EnvVarFlags::EXPORT,
))
} else if key == "history"L {
} else if key == L!("history") {
// Big hack. We only allow getting the history on the main thread. Note that history_t
// may ask for an environment variable, so don't take the lock here (we don't need it).
if (!is_main_thread()) {
@ -359,34 +358,40 @@ impl EnvScopedImpl {
let session_id = history_session_id_from_var(fish_history_var);
History::with_name(&session_id)
});
return Some(EnvVar::new_from_name_vec("history"L, history.get_history()));
} else if key == "fish_killring"L {
Some(EnvVar::new_from_name_vec("fish_killring"L, kill_entries()))
} else if key == "pipestatus"L {
return Some(EnvVar::new_from_name_vec(
L!("history"),
history.get_history(),
));
} else if key == L!("fish_killring") {
Some(EnvVar::new_from_name_vec(
L!("fish_killring"),
kill_entries(),
))
} else if key == L!("pipestatus") {
let js = &self.perproc_data.statuses;
let mut result = Vec::new();
result.reserve(js.pipestatus.len());
for i in &js.pipestatus {
result.push(i.to_wstring());
}
Some(EnvVar::new_from_name_vec("pipestatus"L, result))
} else if key == "status"L {
Some(EnvVar::new_from_name_vec(L!("pipestatus"), result))
} else if key == L!("status") {
let js = &self.perproc_data.statuses;
Some(EnvVar::new_from_name("status"L, js.status.to_wstring()))
} else if key == "status_generation"L {
Some(EnvVar::new_from_name(L!("status"), js.status.to_wstring()))
} else if key == L!("status_generation") {
let status_generation = reader_status_count();
Some(EnvVar::new_from_name(
"status_generation"L,
L!("status_generation"),
status_generation.to_wstring(),
))
} else if key == "fish_kill_signal"L {
} else if key == L!("fish_kill_signal") {
let js = &self.perproc_data.statuses;
let signal = js.kill_signal.map_or(0, |ks| ks.code());
Some(EnvVar::new_from_name(
"fish_kill_signal"L,
L!("fish_kill_signal"),
signal.to_wstring(),
))
} else if key == "umask"L {
} else if key == L!("umask") {
// note umask() is an absurd API: you call it to set the value and it returns the old
// value. Thus we have to call it twice, to reset the value. The env_lock protects
// against races. Guess what the umask is; if we guess right we don't need to reset it.
@ -396,7 +401,7 @@ impl EnvScopedImpl {
if res != guess {
unsafe { libc::umask(res) };
}
Some(EnvVar::new_from_name("umask"L, sprintf!("0%0.3o", res)))
Some(EnvVar::new_from_name(L!("umask"), sprintf!("0%0.3o", res)))
} else {
// We should never get here unless the electric var list is out of sync with the above code.
panic!("Unrecognized computed var name {}", key);

31
src/env/var.rs vendored
View File

@ -1,5 +1,5 @@
use crate::signal::Signal;
use crate::wchar::{widestrs, wstr, WString};
use crate::wchar::{wstr, WString, L};
use crate::wcstringutil::join_strings;
use bitflags::bitflags;
use lazy_static::lazy_static;
@ -253,22 +253,21 @@ pub struct ElectricVar {
// Keep sorted alphabetically
#[rustfmt::skip]
#[widestrs]
pub const ELECTRIC_VARIABLES: &[ElectricVar] = &[
ElectricVar{name: "FISH_VERSION"L, flags: electric::READONLY},
ElectricVar{name: "PWD"L, flags: electric::READONLY | electric::COMPUTED | electric::EXPORTS},
ElectricVar{name: "SHLVL"L, flags: electric::READONLY | electric::EXPORTS},
ElectricVar{name: "_"L, flags: electric::READONLY},
ElectricVar{name: "fish_kill_signal"L, flags:electric::READONLY | electric::COMPUTED},
ElectricVar{name: "fish_killring"L, flags:electric::READONLY | electric::COMPUTED},
ElectricVar{name: "fish_pid"L, flags:electric::READONLY},
ElectricVar{name: "history"L, flags:electric::READONLY | electric::COMPUTED},
ElectricVar{name: "hostname"L, flags:electric::READONLY},
ElectricVar{name: "pipestatus"L, flags:electric::READONLY | electric::COMPUTED},
ElectricVar{name: "status"L, flags:electric::READONLY | electric::COMPUTED},
ElectricVar{name: "status_generation"L, flags:electric::READONLY | electric::COMPUTED},
ElectricVar{name: "umask"L, flags:electric::COMPUTED},
ElectricVar{name: "version"L, flags:electric::READONLY},
ElectricVar{name: L!("FISH_VERSION"), flags: electric::READONLY},
ElectricVar{name: L!("PWD"), flags: electric::READONLY | electric::COMPUTED | electric::EXPORTS},
ElectricVar{name: L!("SHLVL"), flags: electric::READONLY | electric::EXPORTS},
ElectricVar{name: L!("_"), flags: electric::READONLY},
ElectricVar{name: L!("fish_kill_signal"), flags:electric::READONLY | electric::COMPUTED},
ElectricVar{name: L!("fish_killring"), flags:electric::READONLY | electric::COMPUTED},
ElectricVar{name: L!("fish_pid"), flags:electric::READONLY},
ElectricVar{name: L!("history"), flags:electric::READONLY | electric::COMPUTED},
ElectricVar{name: L!("hostname"), flags:electric::READONLY},
ElectricVar{name: L!("pipestatus"), flags:electric::READONLY | electric::COMPUTED},
ElectricVar{name: L!("status"), flags:electric::READONLY | electric::COMPUTED},
ElectricVar{name: L!("status_generation"), flags:electric::READONLY | electric::COMPUTED},
ElectricVar{name: L!("umask"), flags:electric::COMPUTED},
ElectricVar{name: L!("version"), flags:electric::READONLY},
];
assert_sorted_by_name!(ELECTRIC_VARIABLES);

View File

@ -76,16 +76,15 @@ impl EventDescription {
}
}
#[widestrs]
fn name(&self) -> &'static wstr {
match self {
EventDescription::Any => "any"L,
EventDescription::Signal { .. } => "signal"L,
EventDescription::Variable { .. } => "variable"L,
EventDescription::ProcessExit { .. } => "process-exit"L,
EventDescription::JobExit { .. } => "job-exit"L,
EventDescription::CallerExit { .. } => "caller-exit"L,
EventDescription::Generic { .. } => "generic"L,
EventDescription::Any => L!("any"),
EventDescription::Signal { .. } => L!("signal"),
EventDescription::Variable { .. } => L!("variable"),
EventDescription::ProcessExit { .. } => L!("process-exit"),
EventDescription::JobExit { .. } => L!("job-exit"),
EventDescription::CallerExit { .. } => L!("caller-exit"),
EventDescription::Generic { .. } => L!("generic"),
}
}
@ -609,15 +608,14 @@ pub fn fire(parser: &Parser, event: Event) {
}
}
#[widestrs]
pub const EVENT_FILTER_NAMES: [&wstr; 7] = [
"signal"L,
"variable"L,
"exit"L,
"process-exit"L,
"job-exit"L,
"caller-exit"L,
"generic"L,
L!("signal"),
L!("variable"),
L!("exit"),
L!("process-exit"),
L!("job-exit"),
L!("caller-exit"),
L!("generic"),
];
/// Print all events. If type_filter is not empty, only output events with that type.

View File

@ -59,7 +59,6 @@ use std::os::fd::{FromRawFd, RawFd};
use std::slice;
use std::sync::atomic::Ordering;
use std::sync::{atomic::AtomicUsize, Arc};
use widestring_suffix::widestrs;
/// Execute the processes specified by \p j in the parser \p.
/// On a true return, the job was successfully launched and the parser will take responsibility for
@ -472,7 +471,6 @@ fn can_use_posix_spawn_for_job(job: &Job, dup2s: &Dup2List) -> bool {
!wants_terminal
}
#[widestrs]
fn internal_exec(vars: &EnvStack, j: &Job, block_io: IoChain) {
// Do a regular launch - but without forking first...
let mut all_ios = block_io;
@ -499,8 +497,8 @@ fn internal_exec(vars: &EnvStack, j: &Job, block_io: IoChain) {
{
// Decrement SHLVL as we're removing ourselves from the shell "stack".
if is_interactive_session() {
let shlvl_var = vars.getf("SHLVL"L, EnvMode::GLOBAL | EnvMode::EXPORT);
let mut shlvl_str = "0"L.to_owned();
let shlvl_var = vars.getf(L!("SHLVL"), EnvMode::GLOBAL | EnvMode::EXPORT);
let mut shlvl_str = L!("0").to_owned();
if let Some(shlvl_var) = shlvl_var {
if let Ok(shlvl) = fish_wcstol(&shlvl_var.as_string()) {
if shlvl > 0 {
@ -508,7 +506,7 @@ fn internal_exec(vars: &EnvStack, j: &Job, block_io: IoChain) {
}
}
}
vars.set_one("SHLVL"L, EnvMode::GLOBAL | EnvMode::EXPORT, shlvl_str);
vars.set_one(L!("SHLVL"), EnvMode::GLOBAL | EnvMode::EXPORT, shlvl_str);
}
// launch_process _never_ returns.

View File

@ -121,8 +121,7 @@ impl PartialEq<ExpandResultCode> for ExpandResult {
}
/// The string represented by PROCESS_EXPAND_SELF
#[widestrs]
pub const PROCESS_EXPAND_SELF_STR: &wstr = "%self"L;
pub const PROCESS_EXPAND_SELF_STR: &wstr = L!("%self");
/// Perform various forms of expansion on in, such as tilde expansion (\~USER becomes the users home
/// directory), variable expansion (\$VAR_NAME becomes the value of the environment variable
@ -296,26 +295,25 @@ pub fn expand_tilde(input: &mut WString, vars: &dyn Environment) {
}
/// Perform the opposite of tilde expansion on the string, which is modified in place.
#[widestrs]
pub fn replace_home_directory_with_tilde(s: &wstr, vars: &dyn Environment) -> WString {
let mut result = s.to_owned();
// Only absolute paths get this treatment.
if result.starts_with("/"L) {
let mut home_directory = "~"L.to_owned();
if result.starts_with(L!("/")) {
let mut home_directory = L!("~").to_owned();
expand_tilde(&mut home_directory, vars);
// If we can't get a home directory, don't replace anything.
// This is the case e.g. with --no-execute
if home_directory.is_empty() {
return result;
}
if !home_directory.ends_with("/"L) {
if !home_directory.ends_with(L!("/")) {
home_directory.push('/');
}
// Now check if the home_directory prefixes the string.
if result.starts_with(&home_directory) {
// Success
result.replace_range(0..home_directory.len(), "~/"L);
result.replace_range(0..home_directory.len(), L!("~/"));
}
}
result

View File

@ -50,37 +50,36 @@ pub struct FeatureMetadata {
}
/// The metadata, indexed by flag.
#[widestrs]
pub const METADATA: &[FeatureMetadata] = &[
FeatureMetadata {
flag: FeatureFlag::stderr_nocaret,
name: "stderr-nocaret"L,
groups: "3.0"L,
description: "^ no longer redirects stderr (historical, can no longer be changed)"L,
name: L!("stderr-nocaret"),
groups: L!("3.0"),
description: L!("^ no longer redirects stderr (historical, can no longer be changed)"),
default_value: true,
read_only: true,
},
FeatureMetadata {
flag: FeatureFlag::qmark_noglob,
name: "qmark-noglob"L,
groups: "3.0"L,
description: "? no longer globs"L,
name: L!("qmark-noglob"),
groups: L!("3.0"),
description: L!("? no longer globs"),
default_value: false,
read_only: false,
},
FeatureMetadata {
flag: FeatureFlag::string_replace_backslash,
name: "regex-easyesc"L,
groups: "3.1"L,
description: "string replace -r needs fewer \\'s"L,
name: L!("regex-easyesc"),
groups: L!("3.1"),
description: L!("string replace -r needs fewer \\'s"),
default_value: true,
read_only: false,
},
FeatureMetadata {
flag: FeatureFlag::ampersand_nobg_in_token,
name: "ampersand-nobg-in-token"L,
groups: "3.4"L,
description: "& only backgrounds if followed by a separator"L,
name: L!("ampersand-nobg-in-token"),
groups: L!("3.4"),
description: L!("& only backgrounds if followed by a separator"),
default_value: true,
read_only: false,
},
@ -155,9 +154,8 @@ impl Features {
self.values[flag as usize].store(value, Ordering::SeqCst)
}
#[widestrs]
fn set_from_string<'a>(&self, str: &wstr) {
let whitespace = "\t\n\0x0B\0x0C\r "L.as_char_slice();
let whitespace = L!("\t\n\0x0B\0x0C\r ").as_char_slice();
for entry in str.as_char_slice().split(|c| *c == ',') {
if entry.is_empty() {
continue;
@ -169,7 +167,7 @@ impl Features {
&entry[..entry.len() - entry.iter().take_while(|c| whitespace.contains(c)).count()];
// A "no-" prefix inverts the sense.
let (name, value) = match entry.strip_prefix("no-"L.as_char_slice()) {
let (name, value) = match entry.strip_prefix(L!("no-").as_char_slice()) {
Some(suffix) => (suffix, false),
None => (entry, true),
};
@ -186,7 +184,7 @@ impl Features {
}
} else {
for md in METADATA {
if md.groups == name || name == "all"L {
if md.groups == name || name == L!("all") {
if !md.read_only {
self.set(md.flag, value);
}
@ -216,12 +214,11 @@ pub fn scoped_test(flag: FeatureFlag, value: bool, test_fn: impl FnOnce()) {
}
#[test]
#[widestrs]
fn test_feature_flags() {
let f = Features::new();
f.set_from_string("stderr-nocaret,nonsense"L);
f.set_from_string(L!("stderr-nocaret,nonsense"));
assert!(f.test(FeatureFlag::stderr_nocaret));
f.set_from_string("stderr-nocaret,no-stderr-nocaret,nonsense"L);
f.set_from_string(L!("stderr-nocaret,no-stderr-nocaret,nonsense"));
assert!(f.test(FeatureFlag::stderr_nocaret));
// Ensure every metadata is represented once.
@ -235,7 +232,7 @@ fn test_feature_flags() {
assert_eq!(
METADATA[FeatureFlag::stderr_nocaret as usize].name,
"stderr-nocaret"L
L!("stderr-nocaret")
);
}

View File

@ -37,7 +37,6 @@ use libc::{
};
use lru::LruCache;
use rand::Rng;
use widestring_suffix::widestrs;
use crate::{
ast::{Ast, Node},
@ -204,7 +203,6 @@ impl LruCacheExt for LruCache<WString, HistoryItem> {
/// Returns the path for the history file for the given `session_id`, or `None` if it could not be
/// loaded. If `suffix` is provided, append that suffix to the path; this is used for temporary files.
#[widestrs]
fn history_filename(session_id: &wstr, suffix: &wstr) -> Option<WString> {
if session_id.is_empty() {
return None;
@ -216,7 +214,7 @@ fn history_filename(session_id: &wstr, suffix: &wstr) -> Option<WString> {
result.push('/');
result.push_utfstr(session_id);
result.push_utfstr("_history"L);
result.push_utfstr(L!("_history"));
result.push_utfstr(suffix);
Some(result)
}

View File

@ -184,21 +184,20 @@ impl Default for ParseTokenType {
impl ParseTokenType {
/// Return a string describing the token type.
#[widestrs]
pub fn to_wstr(self) -> &'static wstr {
match self {
ParseTokenType::comment => "ParseTokenType::comment"L,
ParseTokenType::error => "ParseTokenType::error"L,
ParseTokenType::tokenizer_error => "ParseTokenType::tokenizer_error"L,
ParseTokenType::background => "ParseTokenType::background"L,
ParseTokenType::end => "ParseTokenType::end"L,
ParseTokenType::pipe => "ParseTokenType::pipe"L,
ParseTokenType::redirection => "ParseTokenType::redirection"L,
ParseTokenType::string => "ParseTokenType::string"L,
ParseTokenType::andand => "ParseTokenType::andand"L,
ParseTokenType::oror => "ParseTokenType::oror"L,
ParseTokenType::terminate => "ParseTokenType::terminate"L,
ParseTokenType::invalid => "ParseTokenType::invalid"L,
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::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"),
}
}
}
@ -211,28 +210,27 @@ impl Default for ParseKeyword {
impl ParseKeyword {
/// Return the keyword as a string.
#[widestrs]
pub fn to_wstr(self) -> &'static wstr {
match self {
ParseKeyword::kw_exclam => "!"L,
ParseKeyword::kw_and => "and"L,
ParseKeyword::kw_begin => "begin"L,
ParseKeyword::kw_builtin => "builtin"L,
ParseKeyword::kw_case => "case"L,
ParseKeyword::kw_command => "command"L,
ParseKeyword::kw_else => "else"L,
ParseKeyword::kw_end => "end"L,
ParseKeyword::kw_exec => "exec"L,
ParseKeyword::kw_for => "for"L,
ParseKeyword::kw_function => "function"L,
ParseKeyword::kw_if => "if"L,
ParseKeyword::kw_in => "in"L,
ParseKeyword::kw_not => "not"L,
ParseKeyword::kw_or => "or"L,
ParseKeyword::kw_switch => "switch"L,
ParseKeyword::kw_time => "time"L,
ParseKeyword::kw_while => "while"L,
_ => "unknown_keyword"L,
ParseKeyword::kw_exclam => L!("!"),
ParseKeyword::kw_and => L!("and"),
ParseKeyword::kw_begin => L!("begin"),
ParseKeyword::kw_builtin => L!("builtin"),
ParseKeyword::kw_case => L!("case"),
ParseKeyword::kw_command => L!("command"),
ParseKeyword::kw_else => L!("else"),
ParseKeyword::kw_end => L!("end"),
ParseKeyword::kw_exec => L!("exec"),
ParseKeyword::kw_for => L!("for"),
ParseKeyword::kw_function => L!("function"),
ParseKeyword::kw_if => L!("if"),
ParseKeyword::kw_in => L!("in"),
ParseKeyword::kw_not => L!("not"),
ParseKeyword::kw_or => L!("or"),
ParseKeyword::kw_switch => L!("switch"),
ParseKeyword::kw_time => L!("time"),
ParseKeyword::kw_while => L!("while"),
_ => L!("unknown_keyword"),
}
}
}
@ -244,27 +242,26 @@ impl printf_compat::args::ToArg<'static> for ParseKeyword {
}
impl From<&wstr> for ParseKeyword {
#[widestrs]
fn from(s: &wstr) -> Self {
match s {
_ if s == "!"L => ParseKeyword::kw_exclam,
_ if s == "and"L => ParseKeyword::kw_and,
_ if s == "begin"L => ParseKeyword::kw_begin,
_ if s == "builtin"L => ParseKeyword::kw_builtin,
_ if s == "case"L => ParseKeyword::kw_case,
_ if s == "command"L => ParseKeyword::kw_command,
_ if s == "else"L => ParseKeyword::kw_else,
_ if s == "end"L => ParseKeyword::kw_end,
_ if s == "exec"L => ParseKeyword::kw_exec,
_ if s == "for"L => ParseKeyword::kw_for,
_ if s == "function"L => ParseKeyword::kw_function,
_ if s == "if"L => ParseKeyword::kw_if,
_ if s == "in"L => ParseKeyword::kw_in,
_ if s == "not"L => ParseKeyword::kw_not,
_ if s == "or"L => ParseKeyword::kw_or,
_ if s == "switch"L => ParseKeyword::kw_switch,
_ if s == "time"L => ParseKeyword::kw_time,
_ if s == "while"L => ParseKeyword::kw_while,
_ if s == L!("!") => ParseKeyword::kw_exclam,
_ if s == L!("and") => ParseKeyword::kw_and,
_ if s == L!("begin") => ParseKeyword::kw_begin,
_ if s == L!("builtin") => ParseKeyword::kw_builtin,
_ if s == L!("case") => ParseKeyword::kw_case,
_ if s == L!("command") => ParseKeyword::kw_command,
_ if s == L!("else") => ParseKeyword::kw_else,
_ if s == L!("end") => ParseKeyword::kw_end,
_ if s == L!("exec") => ParseKeyword::kw_exec,
_ if s == L!("for") => ParseKeyword::kw_for,
_ if s == L!("function") => ParseKeyword::kw_function,
_ if s == L!("if") => ParseKeyword::kw_if,
_ if s == L!("in") => ParseKeyword::kw_in,
_ if s == L!("not") => ParseKeyword::kw_not,
_ if s == L!("or") => ParseKeyword::kw_or,
_ if s == L!("switch") => ParseKeyword::kw_switch,
_ if s == L!("time") => ParseKeyword::kw_time,
_ if s == L!("while") => ParseKeyword::kw_while,
_ => ParseKeyword::none,
}
}
@ -402,27 +399,26 @@ impl ParseError {
}
}
#[widestrs]
pub fn token_type_user_presentable_description(
type_: ParseTokenType,
keyword: ParseKeyword,
) -> WString {
if keyword != ParseKeyword::none {
return sprintf!("keyword: '%ls'"L, keyword.to_wstr());
return sprintf!(L!("keyword: '%ls'"), keyword.to_wstr());
}
match type_ {
ParseTokenType::string => "a string"L.to_owned(),
ParseTokenType::pipe => "a pipe"L.to_owned(),
ParseTokenType::redirection => "a redirection"L.to_owned(),
ParseTokenType::background => "a '&'"L.to_owned(),
ParseTokenType::andand => "'&&'"L.to_owned(),
ParseTokenType::oror => "'||'"L.to_owned(),
ParseTokenType::end => "end of the statement"L.to_owned(),
ParseTokenType::terminate => "end of the input"L.to_owned(),
ParseTokenType::error => "a parse error"L.to_owned(),
ParseTokenType::tokenizer_error => "an incomplete token"L.to_owned(),
ParseTokenType::comment => "a comment"L.to_owned(),
_ => sprintf!("a %ls"L, type_.to_wstr()),
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::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(),
_ => sprintf!(L!("a %ls"), type_.to_wstr()),
}
}

View File

@ -626,15 +626,20 @@ pub fn parse_util_escape_wildcards(s: &wstr) -> WString {
}
/// Checks if the specified string is a help option.
#[widestrs]
pub fn parse_util_argument_is_help(s: &wstr) -> bool {
["-h"L, "--help"L].contains(&s)
[L!("-h"), L!("--help")].contains(&s)
}
/// Returns true if the specified command is a builtin that may not be used in a pipeline.
#[widestrs]
fn parser_is_pipe_forbidden(word: &wstr) -> bool {
["exec"L, "case"L, "break"L, "return"L, "continue"L].contains(&word)
[
L!("exec"),
L!("case"),
L!("break"),
L!("return"),
L!("continue"),
]
.contains(&word)
}
// \return a pointer to the first argument node of an argument_or_redirection_list_t, or nullptr if

View File

@ -46,7 +46,6 @@ use std::sync::{
atomic::{AtomicIsize, AtomicU64, Ordering},
Arc,
};
use widestring_suffix::widestrs;
/// block_t represents a block of commands.
#[derive(Default)]
@ -97,22 +96,21 @@ impl Block {
}
/// Description of the block, for debugging.
#[widestrs]
pub fn description(&self) -> WString {
let mut result = match self.typ() {
BlockType::while_block => "while"L,
BlockType::for_block => "for"L,
BlockType::if_block => "if"L,
BlockType::function_call => "function_call"L,
BlockType::function_call_no_shadow => "function_call_no_shadow"L,
BlockType::switch_block => "switch"L,
BlockType::subst => "substitution"L,
BlockType::top => "top"L,
BlockType::begin => "begin"L,
BlockType::source => "source"L,
BlockType::event => "event"L,
BlockType::breakpoint => "breakpoint"L,
BlockType::variable_assignment => "variable_assignment"L,
BlockType::while_block => L!("while"),
BlockType::for_block => L!("for"),
BlockType::if_block => L!("if"),
BlockType::function_call => L!("function_call"),
BlockType::function_call_no_shadow => L!("function_call_no_shadow"),
BlockType::switch_block => L!("switch"),
BlockType::subst => L!("substitution"),
BlockType::top => L!("top"),
BlockType::begin => L!("begin"),
BlockType::source => L!("source"),
BlockType::event => L!("event"),
BlockType::breakpoint => L!("breakpoint"),
BlockType::variable_assignment => L!("variable_assignment"),
}
.to_owned();

View File

@ -2,33 +2,45 @@
use crate::wchar::prelude::*;
#[widestrs]
const SKIP_KEYWORDS: &[&wstr] = &["else"L, "begin"L];
#[widestrs]
const SKIP_KEYWORDS: &[&wstr] = &[L!("else"), L!("begin")];
const SUBCOMMAND_KEYWORDS: &[&wstr] = &[
"command"L, "builtin"L, "while"L, "exec"L, "if"L, "and"L, "or"L, "not"L, "time"L, "begin"L,
L!("command"),
L!("builtin"),
L!("while"),
L!("exec"),
L!("if"),
L!("and"),
L!("or"),
L!("not"),
L!("time"),
L!("begin"),
];
const BLOCK_KEYWORDS: &[&wstr] = &[
L!("for"),
L!("while"),
L!("if"),
L!("function"),
L!("switch"),
L!("begin"),
];
#[widestrs]
const BLOCK_KEYWORDS: &[&wstr] = &["for"L, "while"L, "if"L, "function"L, "switch"L, "begin"L];
// Don't forget to add any new reserved keywords to the documentation
#[widestrs]
const RESERVED_KEYWORDS: &[&wstr] = &[
"end"L,
"case"L,
"else"L,
"return"L,
"continue"L,
"break"L,
"argparse"L,
"read"L,
"string"L,
"set"L,
"status"L,
"test"L,
"["L,
"_"L,
"eval"L,
L!("end"),
L!("case"),
L!("else"),
L!("return"),
L!("continue"),
L!("break"),
L!("argparse"),
L!("read"),
L!("string"),
L!("set"),
L!("status"),
L!("test"),
L!("["),
L!("_"),
L!("eval"),
];
// The lists above are purposely implemented separately from the logic below, so that future

View File

@ -71,15 +71,14 @@ pub fn path_get_config_remoteness() -> DirRemoteness {
/// Emit any errors if config directories are missing.
/// Use the given environment stack to ensure this only occurs once.
#[widestrs]
pub fn path_emit_config_directory_messages(vars: &EnvStack) {
let data = get_data_directory();
if !data.success() {
maybe_issue_path_warning(
"data"L,
L!("data"),
&wgettext!("can not save history"),
data.used_xdg,
"XDG_DATA_HOME"L,
L!("XDG_DATA_HOME"),
&data.path,
data.err,
vars,
@ -92,10 +91,10 @@ pub fn path_emit_config_directory_messages(vars: &EnvStack) {
let config = get_config_directory();
if !config.success() {
maybe_issue_path_warning(
"config"L,
L!("config"),
&wgettext!("can not save universal variables or functions"),
config.used_xdg,
"XDG_CONFIG_HOME"L,
L!("XDG_CONFIG_HOME"),
&config.path,
config.err,
vars,
@ -110,7 +109,6 @@ pub fn path_emit_config_directory_messages(vars: &EnvStack) {
/// problem, and thus is not central to the behavior of that function. Second, we only want to issue
/// the message once. If the current shell starts a new fish shell (e.g., by running `fish -c` from
/// a function) we don't want that subshell to issue the same warnings.
#[widestrs]
fn maybe_issue_path_warning(
which_dir: &wstr,
custom_error_msg: &wstr,
@ -120,7 +118,7 @@ fn maybe_issue_path_warning(
saved_errno: libc::c_int,
vars: &EnvStack,
) {
let warning_var_name = "_FISH_WARNED_"L.to_owned() + which_dir;
let warning_var_name = L!("_FISH_WARNED_").to_owned() + which_dir;
if vars
.getf(&warning_var_name, EnvMode::GLOBAL | EnvMode::EXPORT)
.is_some()
@ -130,7 +128,7 @@ fn maybe_issue_path_warning(
vars.set_one(
&warning_var_name,
EnvMode::GLOBAL | EnvMode::EXPORT,
"1"L.to_owned(),
L!("1").to_owned(),
);
FLOG!(error, custom_error_msg);
@ -147,7 +145,7 @@ fn maybe_issue_path_warning(
)
);
} else {
let env_var = if using_xdg { xdg_var } else { "HOME"L };
let env_var = if using_xdg { xdg_var } else { L!("HOME") };
FLOG!(
warning_path,
wgettext_fmt!(
@ -184,13 +182,12 @@ pub fn path_get_path(cmd: &wstr, vars: &dyn Environment) -> Option<WString> {
}
// PREFIX is defined at build time.
#[widestrs]
pub static DEFAULT_PATH: Lazy<[WString; 3]> = Lazy::new(|| {
[
// TODO This should use env!. The fallback is only to appease "cargo test" for now.
WString::from_str(option_env!("PREFIX").unwrap_or("/usr/local")) + "/bin"L,
"/usr/bin"L.to_owned(),
"/bin"L.to_owned(),
WString::from_str(option_env!("PREFIX").unwrap_or("/usr/local")) + L!("/bin"),
L!("/usr/bin").to_owned(),
L!("/bin").to_owned(),
]
});
@ -357,23 +354,25 @@ pub fn path_get_cdpath(dir: &wstr, wd: &wstr, vars: &dyn Environment) -> Option<
}
/// Returns the given directory with all CDPATH components applied.
#[widestrs]
pub fn path_apply_cdpath(dir: &wstr, wd: &wstr, env_vars: &dyn Environment) -> Vec<WString> {
let mut paths = vec![];
if dir.chars().next() == Some('/') {
// Absolute path.
paths.push(dir.to_owned());
} else if dir.starts_with("./"L) || dir.starts_with("../"L) || ["."L, ".."L].contains(&dir) {
} else if dir.starts_with(L!("./"))
|| dir.starts_with(L!("../"))
|| [L!("."), L!("..")].contains(&dir)
{
// Path is relative to the working directory.
paths.push(path_normalize_for_cd(wd, dir));
} else {
// Respect CDPATH.
let mut cdpathsv = vec![];
if let Some(cdpaths) = env_vars.get("CDPATH"L) {
if let Some(cdpaths) = env_vars.get(L!("CDPATH")) {
cdpathsv = cdpaths.as_list().to_vec();
}
// Always append $PWD
cdpathsv.push("."L.to_owned());
cdpathsv.push(L!(".").to_owned());
for path in cdpathsv {
let mut abspath = WString::new();
// We want to return an absolute path (see issue 6220)
@ -399,15 +398,14 @@ pub fn path_apply_cdpath(dir: &wstr, wd: &wstr, env_vars: &dyn Environment) -> V
/// Returns the path resolved as an implicit cd command, or none() if none. This requires it to
/// start with one of the allowed prefixes (., .., ~) and resolve to a directory.
#[widestrs]
pub fn path_as_implicit_cd(path: &wstr, wd: &wstr, vars: &dyn Environment) -> Option<WString> {
let mut exp_path = path.to_owned();
expand_tilde(&mut exp_path, vars);
if exp_path.starts_with("/"L)
|| exp_path.starts_with("./"L)
|| exp_path.starts_with("../"L)
|| exp_path.ends_with("/"L)
|| exp_path == ".."L
if exp_path.starts_with(L!("/"))
|| exp_path.starts_with(L!("./"))
|| exp_path.starts_with(L!("../"))
|| exp_path.ends_with(L!("/"))
|| exp_path == L!("..")
{
// These paths can be implicit cd, so see if you cd to the path. Note that a single period
// cannot (that's used for sourcing files anyways).
@ -495,15 +493,14 @@ pub fn paths_are_equivalent(p1: &wstr, p2: &wstr) -> bool {
idx1 == len1 && idx2 == len2
}
#[widestrs]
pub fn path_is_valid(path: &wstr, working_directory: &wstr) -> bool {
// Some special paths are always valid.
if path.is_empty() {
false
} else if ["."L, "./"L].contains(&path) {
} else if [L!("."), L!("./")].contains(&path) {
true
} else if [".."L, "../"L].contains(&path) {
!working_directory.is_empty() && working_directory != "/"L
} else if [L!(".."), L!("../")].contains(&path) {
!working_directory.is_empty() && working_directory != L!("/")
} else if path.chars().next() != Some('/') {
// Prepend the working directory. Note that we know path is not empty here.
let mut tmp = working_directory.to_owned();
@ -582,7 +579,6 @@ impl BaseDirectory {
/// Attempt to get a base directory, creating it if necessary. If a variable named \p xdg_var is
/// set, use that directory; otherwise use the path \p non_xdg_homepath rooted in $HOME. \return the
/// result; see the base_directory_t fields.
#[widestrs]
fn make_base_directory(xdg_var: &wstr, non_xdg_homepath: &wstr) -> BaseDirectory {
// The vars we fetch must be exported. Allowing them to be universal doesn't make sense and
// allowing that creates a lock inversion that deadlocks the shell since we're called before
@ -592,10 +588,10 @@ fn make_base_directory(xdg_var: &wstr, non_xdg_homepath: &wstr) -> BaseDirectory
let mut path = WString::new();
let used_xdg;
if let Some(xdg_dir) = vars.getf_unless_empty(xdg_var, EnvMode::GLOBAL | EnvMode::EXPORT) {
path = xdg_dir.as_string() + "/fish"L;
path = xdg_dir.as_string() + L!("/fish");
used_xdg = true;
} else {
if let Some(home) = vars.getf_unless_empty("HOME"L, EnvMode::GLOBAL | EnvMode::EXPORT) {
if let Some(home) = vars.getf_unless_empty(L!("HOME"), EnvMode::GLOBAL | EnvMode::EXPORT) {
path = home.as_string() + non_xdg_homepath;
}
used_xdg = false;
@ -706,17 +702,15 @@ fn path_remoteness(path: &wstr) -> DirRemoteness {
}
}
#[widestrs]
fn get_data_directory() -> &'static BaseDirectory {
static DIR: Lazy<BaseDirectory> =
Lazy::new(|| make_base_directory("XDG_DATA_HOME"L, "/.local/share/fish"L));
Lazy::new(|| make_base_directory(L!("XDG_DATA_HOME"), L!("/.local/share/fish")));
&*DIR
}
#[widestrs]
fn get_config_directory() -> &'static BaseDirectory {
static DIR: Lazy<BaseDirectory> =
Lazy::new(|| make_base_directory("XDG_CONFIG_HOME"L, "/.config/fish"L));
Lazy::new(|| make_base_directory(L!("XDG_CONFIG_HOME"), L!("/.config/fish")));
&*DIR
}

View File

@ -38,7 +38,6 @@ use std::os::fd::RawFd;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, AtomicU8, Ordering};
use std::sync::{Arc, Mutex};
use widestring_suffix::widestrs;
/// Types of processes.
#[derive(Default, Eq, PartialEq)]
@ -844,15 +843,14 @@ impl Job {
/// due to reuse of freed job ids. Prevents overloading the debug comments with the full,
/// untruncated job string when we don't care what the job is, only which of the currently
/// running jobs it is.
#[widestrs]
pub fn preview(&self) -> WString {
if self.processes().is_empty() {
return ""L.to_owned();
return L!("").to_owned();
}
// Note argv0 may be empty in e.g. a block process.
let procs = self.processes();
let result = procs.first().unwrap().argv0().unwrap_or("null"L);
result.to_owned() + "..."L
let result = procs.first().unwrap().argv0().unwrap_or(L!("null"));
result.to_owned() + L!("...")
}
/// \return our pgid, or none if we don't have one, or are internal to fish
@ -1185,7 +1183,6 @@ pub fn jobs_requiring_warning_on_exit(parser: &Parser) -> JobList {
/// Print the exit warning for the given jobs, which should have been obtained via
/// jobs_requiring_warning_on_exit().
#[widestrs]
pub fn print_exit_warning_for_jobs(jobs: &JobList) {
printf!("%s", wgettext!("There are still jobs active:\n"));
printf!("%s", wgettext!("\n PID Command\n"));
@ -1581,9 +1578,8 @@ fn call_job_summary(parser: &Parser, cmd: &wstr) {
// \return a command which invokes fish_job_summary.
// The process pointer may be null, in which case it represents the entire job.
// Note this implements the arguments which fish_job_summary expects.
#[widestrs]
fn summary_command(j: &Job, p: Option<&Process>) -> WString {
let mut buffer = "fish_job_summary"L.to_owned();
let mut buffer = L!("fish_job_summary").to_owned();
// Job id.
buffer += &sprintf!(" %s", j.job_id().to_wstring())[..];
@ -1599,9 +1595,9 @@ fn summary_command(j: &Job, p: Option<&Process>) -> WString {
None => {
// No process, we are summarizing the whole job.
buffer += if j.is_stopped() {
" STOPPED"L
L!(" STOPPED")
} else {
" ENDED"L
L!(" ENDED")
};
}
Some(p) => {

View File

@ -357,60 +357,59 @@ impl LookupEntry {
// Lookup table used to convert between signal names and signal ids, etc.
#[rustfmt::skip]
#[widestrs]
const SIGNAL_TABLE : &[LookupEntry] = &[
LookupEntry::new(libc::SIGHUP, "SIGHUP"L, "Terminal hung up"L),
LookupEntry::new(libc::SIGINT, "SIGINT"L, "Quit request from job control (^C)"L),
LookupEntry::new(libc::SIGQUIT, "SIGQUIT"L, "Quit request from job control with core dump (^\\)"L),
LookupEntry::new(libc::SIGILL, "SIGILL"L, "Illegal instruction"L),
LookupEntry::new(libc::SIGTRAP, "SIGTRAP"L, "Trace or breakpoint trap"L),
LookupEntry::new(libc::SIGABRT, "SIGABRT"L, "Abort"L),
LookupEntry::new(libc::SIGBUS, "SIGBUS"L, "Misaligned address error"L),
LookupEntry::new(libc::SIGFPE, "SIGFPE"L, "Floating point exception"L),
LookupEntry::new(libc::SIGKILL, "SIGKILL"L, "Forced quit"L),
LookupEntry::new(libc::SIGUSR1, "SIGUSR1"L, "User defined signal 1"L),
LookupEntry::new(libc::SIGUSR2, "SIGUSR2"L, "User defined signal 2"L),
LookupEntry::new(libc::SIGSEGV, "SIGSEGV"L, "Address boundary error"L),
LookupEntry::new(libc::SIGPIPE, "SIGPIPE"L, "Broken pipe"L),
LookupEntry::new(libc::SIGALRM, "SIGALRM"L, "Timer expired"L),
LookupEntry::new(libc::SIGTERM, "SIGTERM"L, "Polite quit request"L),
LookupEntry::new(libc::SIGCHLD, "SIGCHLD"L, "Child process status changed"L),
LookupEntry::new(libc::SIGCONT, "SIGCONT"L, "Continue previously stopped process"L),
LookupEntry::new(libc::SIGSTOP, "SIGSTOP"L, "Forced stop"L),
LookupEntry::new(libc::SIGTSTP, "SIGTSTP"L, "Stop request from job control (^Z)"L),
LookupEntry::new(libc::SIGTTIN, "SIGTTIN"L, "Stop from terminal input"L),
LookupEntry::new(libc::SIGTTOU, "SIGTTOU"L, "Stop from terminal output"L),
LookupEntry::new(libc::SIGURG, "SIGURG"L, "Urgent socket condition"L),
LookupEntry::new(libc::SIGXCPU, "SIGXCPU"L, "CPU time limit exceeded"L),
LookupEntry::new(libc::SIGXFSZ, "SIGXFSZ"L, "File size limit exceeded"L),
LookupEntry::new(libc::SIGVTALRM, "SIGVTALRM"L, "Virtual timefr expired"L),
LookupEntry::new(libc::SIGPROF, "SIGPROF"L, "Profiling timer expired"L),
LookupEntry::new(libc::SIGWINCH, "SIGWINCH"L, "Window size change"L),
LookupEntry::new(libc::SIGIO, "SIGIO"L, "I/O on asynchronous file descriptor is possible"L),
LookupEntry::new(libc::SIGSYS, "SIGSYS"L, "Bad system call"L),
LookupEntry::new(libc::SIGIOT, "SIGIOT"L, "Abort (Alias for SIGABRT)"L),
LookupEntry::new(libc::SIGHUP, L!("SIGHUP"), L!("Terminal hung up")),
LookupEntry::new(libc::SIGINT, L!("SIGINT"), L!("Quit request from job control (^C)")),
LookupEntry::new(libc::SIGQUIT, L!("SIGQUIT"), L!("Quit request from job control with core dump (^\\)")),
LookupEntry::new(libc::SIGILL, L!("SIGILL"), L!("Illegal instruction")),
LookupEntry::new(libc::SIGTRAP, L!("SIGTRAP"), L!("Trace or breakpoint trap")),
LookupEntry::new(libc::SIGABRT, L!("SIGABRT"), L!("Abort")),
LookupEntry::new(libc::SIGBUS, L!("SIGBUS"), L!("Misaligned address error")),
LookupEntry::new(libc::SIGFPE, L!("SIGFPE"), L!("Floating point exception")),
LookupEntry::new(libc::SIGKILL, L!("SIGKILL"), L!("Forced quit")),
LookupEntry::new(libc::SIGUSR1, L!("SIGUSR1"), L!("User defined signal 1")),
LookupEntry::new(libc::SIGUSR2, L!("SIGUSR2"), L!("User defined signal 2")),
LookupEntry::new(libc::SIGSEGV, L!("SIGSEGV"), L!("Address boundary error")),
LookupEntry::new(libc::SIGPIPE, L!("SIGPIPE"), L!("Broken pipe")),
LookupEntry::new(libc::SIGALRM, L!("SIGALRM"), L!("Timer expired")),
LookupEntry::new(libc::SIGTERM, L!("SIGTERM"), L!("Polite quit request")),
LookupEntry::new(libc::SIGCHLD, L!("SIGCHLD"), L!("Child process status changed")),
LookupEntry::new(libc::SIGCONT, L!("SIGCONT"), L!("Continue previously stopped process")),
LookupEntry::new(libc::SIGSTOP, L!("SIGSTOP"), L!("Forced stop")),
LookupEntry::new(libc::SIGTSTP, L!("SIGTSTP"), L!("Stop request from job control (^Z)")),
LookupEntry::new(libc::SIGTTIN, L!("SIGTTIN"), L!("Stop from terminal input")),
LookupEntry::new(libc::SIGTTOU, L!("SIGTTOU"), L!("Stop from terminal output")),
LookupEntry::new(libc::SIGURG, L!("SIGURG"), L!("Urgent socket condition")),
LookupEntry::new(libc::SIGXCPU, L!("SIGXCPU"), L!("CPU time limit exceeded")),
LookupEntry::new(libc::SIGXFSZ, L!("SIGXFSZ"), L!("File size limit exceeded")),
LookupEntry::new(libc::SIGVTALRM, L!("SIGVTALRM"), L!("Virtual timefr expired")),
LookupEntry::new(libc::SIGPROF, L!("SIGPROF"), L!("Profiling timer expired")),
LookupEntry::new(libc::SIGWINCH, L!("SIGWINCH"), L!("Window size change")),
LookupEntry::new(libc::SIGIO, L!("SIGIO"), L!("I/O on asynchronous file descriptor is possible")),
LookupEntry::new(libc::SIGSYS, L!("SIGSYS"), L!("Bad system call")),
LookupEntry::new(libc::SIGIOT, L!("SIGIOT"), L!("Abort (Alias for SIGABRT)")),
#[cfg(any(feature = "bsd", target_os = "macos"))]
LookupEntry::new(libc::SIGEMT, "SIGEMT"L, "Unused signal"L),
LookupEntry::new(libc::SIGEMT, L!("SIGEMT"), L!("Unused signal")),
#[cfg(any(feature = "bsd", target_os = "macos"))]
LookupEntry::new(libc::SIGINFO, "SIGINFO"L, "Information request"L),
LookupEntry::new(libc::SIGINFO, L!("SIGINFO"), L!("Information request")),
#[cfg(target_os = "linux")]
LookupEntry::new(libc::SIGSTKFLT, "SISTKFLT"L, "Stack fault"L),
LookupEntry::new(libc::SIGSTKFLT, L!("SISTKFLT"), L!("Stack fault")),
#[cfg(target_os = "linux")]
LookupEntry::new(libc::SIGIOT, "SIGIOT"L, "Abort (Alias for SIGABRT)"L),
LookupEntry::new(libc::SIGIOT, L!("SIGIOT"), L!("Abort (Alias for SIGABRT)")),
#[cfg(target_os = "linux")]
#[allow(deprecated)]
LookupEntry::new(libc::SIGUNUSED, "SIGUNUSED"L, "Unused signal"L),
LookupEntry::new(libc::SIGUNUSED, L!("SIGUNUSED"), L!("Unused signal")),
#[cfg(target_os = "linux")]
LookupEntry::new(libc::SIGPWR, "SIGPWR"L, "Power failure"L),
LookupEntry::new(libc::SIGPWR, L!("SIGPWR"), L!("Power failure")),
// TODO: determine whether SIGWIND is defined on any platform.
//LookupEntry::new(libc::SIGWIND, "SIGWIND"L, "Window size change"L),
//LookupEntry::new(libc::SIGWIND, L!("SIGWIND"), L!("Window size change")),
];
// Return true if two strings are equal, ignoring ASCII case.

View File

@ -5,7 +5,6 @@ use crate::wchar::prelude::*;
use crate::wutil::wgetcwd;
use std::collections::HashMap;
use std::time::{SystemTime, UNIX_EPOCH};
use widestring_suffix::widestrs;
/// An environment built around an std::map.
#[derive(Clone, Default)]
@ -40,10 +39,9 @@ impl PwdEnvironment {
Self::default()
}
}
#[widestrs]
impl Environment for PwdEnvironment {
fn getf(&self, name: &wstr, mode: EnvMode) -> Option<EnvVar> {
if name == "PWD"L {
if name == L!("PWD") {
return Some(EnvVar::new(wgetcwd(), EnvVarFlags::default()));
}
self.parent.getf(name, mode)
@ -51,8 +49,8 @@ impl Environment for PwdEnvironment {
fn get_names(&self, flags: EnvMode) -> Vec<WString> {
let mut res = self.parent.get_names(flags);
if !res.iter().any(|n| n == "PWD"L) {
res.push("PWD"L.to_owned());
if !res.iter().any(|n| n == L!("PWD")) {
res.push(L!("PWD").to_owned());
}
res
}

View File

@ -2,7 +2,7 @@ use crate::common::{
escape_string, str2wcstring, unescape_string, wcs2string, EscapeFlags, EscapeStringStyle,
UnescapeStringStyle, ENCODE_DIRECT_BASE, ENCODE_DIRECT_END,
};
use crate::wchar::{widestrs, wstr, WString};
use crate::wchar::{wstr, WString, L};
use crate::wutil::encoding::{wcrtomb, zero_mbstate, AT_LEAST_MB_LEN_MAX};
use rand::{Rng, RngCore};
use rand_pcg::Pcg64Mcg;
@ -31,41 +31,39 @@ fn setlocale() {
panic!("No UTF-8 locale found");
}
#[widestrs]
#[test]
fn test_escape_string() {
let regex = |input| escape_string(input, EscapeStringStyle::Regex);
// plain text should not be needlessly escaped
assert_eq!(regex("hello world!"L), "hello world!"L);
assert_eq!(regex(L!("hello world!")), L!("hello world!"));
// all the following are intended to be ultimately matched literally - even if they
// don't look like that's the intent - so we escape them.
assert_eq!(regex(".ext"L), "\\.ext"L);
assert_eq!(regex("{word}"L), "\\{word\\}"L);
assert_eq!(regex("hola-mundo"L), "hola\\-mundo"L);
assert_eq!(regex(L!(".ext")), L!("\\.ext"));
assert_eq!(regex(L!("{word}")), L!("\\{word\\}"));
assert_eq!(regex(L!("hola-mundo")), L!("hola\\-mundo"));
assert_eq!(
regex("$17.42 is your total?"L),
"\\$17\\.42 is your total\\?"L
regex(L!("$17.42 is your total?")),
L!("\\$17\\.42 is your total\\?")
);
assert_eq!(
regex("not really escaped\\?"L),
"not really escaped\\\\\\?"L
regex(L!("not really escaped\\?")),
L!("not really escaped\\\\\\?")
);
}
#[widestrs]
#[test]
pub fn test_unescape_sane() {
const TEST_CASES: &[(&wstr, &wstr)] = &[
("abcd"L, "abcd"L),
("'abcd'"L, "abcd"L),
("'abcd\\n'"L, "abcd\\n"L),
("\"abcd\\n\""L, "abcd\\n"L),
("\"abcd\\n\""L, "abcd\\n"L),
("\\143"L, "c"L),
("'\\143'"L, "\\143"L),
("\\n"L, "\n"L), // \n normally becomes newline
(L!("abcd"), L!("abcd")),
(L!("'abcd'"), L!("abcd")),
(L!("'abcd\\n'"), L!("abcd\\n")),
(L!("\"abcd\\n\""), L!("abcd\\n")),
(L!("\"abcd\\n\""), L!("abcd\\n")),
(L!("\\143"), L!("c")),
(L!("'\\143'"), L!("\\143")),
(L!("\\n"), L!("\n")), // \n normally becomes newline
];
for (input, expected) in TEST_CASES {
@ -80,17 +78,16 @@ pub fn test_unescape_sane() {
}
}
#[widestrs]
#[test]
fn test_escape_var() {
const TEST_CASES: &[(&wstr, &wstr)] = &[
(" a"L, "_20_a"L),
("a B "L, "a_20_42_20_"L),
("a b "L, "a_20_b_20_"L),
(" B"L, "_20_42_"L),
(" f"L, "_20_f"L),
(" 1"L, "_20_31_"L),
("a\nghi_"L, "a_0A_ghi__"L),
(L!(" a"), L!("_20_a")),
(L!("a B "), L!("a_20_42_20_")),
(L!("a b "), L!("a_20_b_20_")),
(L!(" B"), L!("_20_42_")),
(L!(" f"), L!("_20_f")),
(L!(" 1"), L!("_20_31_")),
(L!("a\nghi_"), L!("a_0A_ghi__")),
];
for (input, expected) in TEST_CASES {
@ -142,11 +139,10 @@ fn test_escape_random_url() {
escape_test(EscapeStringStyle::Url, UnescapeStringStyle::Url);
}
#[widestrs]
#[test]
fn test_escape_no_printables() {
// Verify that ESCAPE_NO_PRINTABLES also escapes backslashes so we don't regress on issue #3892.
let random_string = "line 1\\n\nline 2"L.to_owned();
let random_string = L!("line 1\\n\nline 2").to_owned();
let escaped_string = escape_string(
&random_string,
EscapeStringStyle::Script(EscapeFlags::NO_PRINTABLES | EscapeFlags::NO_QUOTED),

View File

@ -95,7 +95,6 @@ pub enum ErrorKind {
Unknown,
}
#[widestrs]
impl ErrorKind {
pub fn describe_wstr(&self) -> &'static wstr {
match self {
@ -273,50 +272,49 @@ fn npr(n: f64, r: f64) -> f64 {
ncr(n, r) * fac(r)
}
#[widestrs]
const BUILTINS: &[(&wstr, Function)] = &[
// must be in alphabetical order
("abs"L, Function::Fn1(f64::abs)),
("acos"L, Function::Fn1(f64::acos)),
("asin"L, Function::Fn1(f64::asin)),
("atan"L, Function::Fn1(f64::atan)),
("atan2"L, Function::Fn2(f64::atan2)),
(L!("abs"), Function::Fn1(f64::abs)),
(L!("acos"), Function::Fn1(f64::acos)),
(L!("asin"), Function::Fn1(f64::asin)),
(L!("atan"), Function::Fn1(f64::atan)),
(L!("atan2"), Function::Fn2(f64::atan2)),
(
"bitand"L,
L!("bitand"),
Function::Fn2(|a, b| bitwise_op(a, b, BitAnd::bitand)),
),
(
"bitor"L,
L!("bitor"),
Function::Fn2(|a, b| bitwise_op(a, b, BitOr::bitor)),
),
(
"bitxor"L,
L!("bitxor"),
Function::Fn2(|a, b| bitwise_op(a, b, BitXor::bitxor)),
),
("ceil"L, Function::Fn1(f64::ceil)),
("cos"L, Function::Fn1(f64::cos)),
("cosh"L, Function::Fn1(f64::cosh)),
("e"L, Function::Constant(E)),
("exp"L, Function::Fn1(f64::exp)),
("fac"L, Function::Fn1(fac)),
("floor"L, Function::Fn1(f64::floor)),
("ln"L, Function::Fn1(f64::ln)),
("log"L, Function::Fn1(f64::log10)),
("log10"L, Function::Fn1(f64::log10)),
("log2"L, Function::Fn1(f64::log2)),
("max"L, Function::FnN(maximum)),
("min"L, Function::FnN(minimum)),
("ncr"L, Function::Fn2(ncr)),
("npr"L, Function::Fn2(npr)),
("pi"L, Function::Constant(PI)),
("pow"L, Function::Fn2(f64::powf)),
("round"L, Function::Fn1(f64::round)),
("sin"L, Function::Fn1(f64::sin)),
("sinh"L, Function::Fn1(f64::sinh)),
("sqrt"L, Function::Fn1(f64::sqrt)),
("tan"L, Function::Fn1(f64::tan)),
("tanh"L, Function::Fn1(f64::tanh)),
("tau"L, Function::Constant(TAU)),
(L!("ceil"), Function::Fn1(f64::ceil)),
(L!("cos"), Function::Fn1(f64::cos)),
(L!("cosh"), Function::Fn1(f64::cosh)),
(L!("e"), Function::Constant(E)),
(L!("exp"), Function::Fn1(f64::exp)),
(L!("fac"), Function::Fn1(fac)),
(L!("floor"), Function::Fn1(f64::floor)),
(L!("ln"), Function::Fn1(f64::ln)),
(L!("log"), Function::Fn1(f64::log10)),
(L!("log10"), Function::Fn1(f64::log10)),
(L!("log2"), Function::Fn1(f64::log2)),
(L!("max"), Function::FnN(maximum)),
(L!("min"), Function::FnN(minimum)),
(L!("ncr"), Function::Fn2(ncr)),
(L!("npr"), Function::Fn2(npr)),
(L!("pi"), Function::Constant(PI)),
(L!("pow"), Function::Fn2(f64::powf)),
(L!("round"), Function::Fn1(f64::round)),
(L!("sin"), Function::Fn1(f64::sin)),
(L!("sinh"), Function::Fn1(f64::sinh)),
(L!("sqrt"), Function::Fn1(f64::sqrt)),
(L!("tan"), Function::Fn1(f64::tan)),
(L!("tanh"), Function::Fn1(f64::tanh)),
(L!("tau"), Function::Constant(TAU)),
];
assert_sorted_by_name!(BUILTINS, 0);

View File

@ -143,10 +143,9 @@ pub const TOK_SHOW_BLANK_LINES: TokFlags = TokFlags(4);
pub const TOK_CONTINUE_AFTER_ERROR: TokFlags = TokFlags(8);
impl From<TokenizerError> for &'static wstr {
#[widestrs]
fn from(err: TokenizerError) -> Self {
match err {
TokenizerError::none => ""L,
TokenizerError::none => L!(""),
TokenizerError::unterminated_quote => {
wgettext!("Unexpected end of string, quotes are not balanced")
}

View File

@ -32,7 +32,6 @@ use std::mem;
use std::pin::Pin;
use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::{Condvar, Mutex, MutexGuard};
use widestring_suffix::widestrs;
/// The list of topics which may be observed.
#[repr(u8)]
@ -73,7 +72,6 @@ pub fn all_topics() -> [topic_t; 3] {
[topic_t::sighupint, topic_t::sigchld, topic_t::internal_exit]
}
#[widestrs]
impl GenerationsList {
pub fn new() -> Self {
Self::default()

View File

@ -16,7 +16,6 @@ pub mod prelude {
wchar_ext::{ToWString, WExt},
wutil::{sprintf, wgettext, wgettext_fmt, wgettext_maybe_fmt, wgettext_str},
};
pub use widestring_suffix::widestrs;
}
/// Creates a wstr string slice, like the "L" prefix of C++.
@ -30,17 +29,6 @@ macro_rules! L {
}
pub use L;
/// A proc-macro for creating wide string literals using an L *suffix*.
/// Example usage:
/// ```ignore
/// #[widestrs]
/// pub fn func() {
/// let s = "hello"L; // type &'static wstr
/// }
/// ```
/// Note: the resulting string is NOT nul-terminated.
pub use widestring_suffix::widestrs;
/// Encode a literal byte in a UTF-32 character. This is required for e.g. the echo builtin, whose
/// escape sequences can be used to construct raw byte sequences which are then interpreted as e.g.
/// UTF-8 by the terminal. If we were to interpret each of those bytes as a codepoint and encode it

View File

@ -27,7 +27,6 @@ use std::io::{self, Write};
use std::os::unix::prelude::*;
pub use wcstoi::*;
use widestring_suffix::widestrs;
/// Wide character version of opendir(). Note that opendir() is guaranteed to set close-on-exec by
/// POSIX (hooray).
@ -274,7 +273,6 @@ fn test_normalize_path() {
/// appropriate for cd. That is, return effectively wd + path while resolving leading ../s from
/// path. The intent here is to allow 'cd' out of a directory which may no longer exist, without
/// allowing 'cd' into a directory that may not exist; see #5341.
#[widestrs]
pub fn path_normalize_for_cd(wd: &wstr, path: &wstr) -> WString {
// Fast paths.
const sep: char = '/';
@ -302,9 +300,9 @@ pub fn path_normalize_for_cd(wd: &wstr, path: &wstr) -> WString {
let mut erase_count = 0;
for comp in &path_comps {
let mut erase_it = false;
if comp.is_empty() || comp == "."L {
if comp.is_empty() || comp == L!(".") {
erase_it = true;
} else if comp == ".."L && !wd_comps.is_empty() {
} else if comp == L!("..") && !wd_comps.is_empty() {
erase_it = true;
wd_comps.pop();
}

View File

@ -8,23 +8,22 @@ use crate::fallback::fish_mkstemp_cloexec;
use super::*;
#[test]
#[widestrs]
fn test_wdirname_wbasename() {
// path, dir, base
struct Test(&'static wstr, &'static wstr, &'static wstr);
const testcases: &[Test] = &[
Test(""L, "."L, "."L),
Test("foo//"L, "."L, "foo"L),
Test("foo//////"L, "."L, "foo"L),
Test("/////foo"L, "/"L, "foo"L),
Test("//foo/////bar"L, "//foo"L, "bar"L),
Test("foo/////bar"L, "foo"L, "bar"L),
Test(L!(""), L!("."), L!(".")),
Test(L!("foo//"), L!("."), L!("foo")),
Test(L!("foo//////"), L!("."), L!("foo")),
Test(L!("/////foo"), L!("/"), L!("foo")),
Test(L!("//foo/////bar"), L!("//foo"), L!("bar")),
Test(L!("foo/////bar"), L!("foo"), L!("bar")),
// Examples given in XPG4.2.
Test("/usr/lib"L, "/usr"L, "lib"L),
Test("usr"L, "."L, "usr"L),
Test("/"L, "/"L, "/"L),
Test("."L, "."L, "."L),
Test(".."L, "."L, ".."L),
Test(L!("/usr/lib"), L!("/usr"), L!("lib")),
Test(L!("usr"), L!("."), L!("usr")),
Test(L!("/"), L!("/"), L!("/")),
Test(L!("."), L!("."), L!(".")),
Test(L!(".."), L!("."), L!("..")),
];
for tc in testcases {
@ -54,7 +53,7 @@ fn test_wdirname_wbasename() {
let last_slash = longpath.chars().rposition(|c| c == '/').unwrap();
let longpath_dir = &longpath[..last_slash];
assert_eq!(wdirname(&longpath), longpath_dir);
assert_eq!(wbasename(&longpath), "overlong"L);
assert_eq!(wbasename(&longpath), L!("overlong"));
}
#[test]