Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions src/runtime/shell/builtin/touch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,17 +349,20 @@ pub struct Opts {}

impl FlagParser for Opts {
fn parse_long(&mut self, flag: &[u8]) -> Option<ParseFlagResult> {
match flag {
b"--no-create" => Some(ParseFlagResult::Unsupported(unsupported_flag(
b"--no-create",
))),
// `--option=VALUE` spells the same option as `--option VALUE`.
let (name, value) = match bun_core::strings::split_once_char(flag, b'=') {
Some((name, value)) => (name, Some(value)),
None => (flag, None),
};
match name {
b"--no-create" if value.is_none() => Some(ParseFlagResult::Unsupported(
unsupported_flag(b"--no-create"),
)),
b"--date" => Some(ParseFlagResult::Unsupported(unsupported_flag(b"--date"))),
b"--reference" => Some(ParseFlagResult::Unsupported(unsupported_flag(
b"--reference=FILE",
))),
b"--time" => Some(ParseFlagResult::Unsupported(unsupported_flag(
b"--reference=FILE",
))),
b"--time" => Some(ParseFlagResult::Unsupported(unsupported_flag(b"--time"))),
_ => None,
}
}
Expand Down
54 changes: 54 additions & 0 deletions test/js/bun/shell/commands/touch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { $ } from "bun";
import { describe, expect } from "bun:test";
import { createTestBuilder } from "../test_builder";
const TestBuilder = createTestBuilder(import.meta.path);

$.nothrow();

describe.concurrent("bunshell touch", () => {
describe("unsupported options are reported under the name they were given", () => {
// option as typed -> how the error names it (--reference's usage suffix predates this table)
const unsupported = {
"--no-create": "--no-create",
"--date": "--date",
"--reference": "--reference=FILE",
"--time": "--time",
// --date, --reference and --time take a value, which GNU touch also accepts as `--option=VALUE`.
"--date=@0": "--date",
"--date=": "--date",
"--reference=other": "--reference=FILE",
"--time=atime": "--time",
"-a": "-a",
"-c": "-c",
"-d": "-d",
"-h": "-h",
"-m": "-m",
"-r": "-r",
"-t": "-t",
};

for (const [option, reported] of Object.entries(unsupported)) {
TestBuilder.command`touch ${option} file`
.ensureTempDir()
.quiet()
.stdout("")
.stderr(`touch: unsupported option, please open a GitHub issue -- ${reported}\n`)
.exitCode(1)
.doesNotExist("file")
.runAsTest(option);
}
});

describe("options that take no value do not accept one", () => {
// `--no-create` is a plain flag, so `--no-create=VALUE` is not a spelling of it. Which bytes the
// illegal option message quotes is up to the shared flag parser, so only the classification is pinned.
TestBuilder.command`touch --no-create=1 file`
.ensureTempDir()
.quiet()
.stdout("")
.stderr(stderr => expect(stderr).toStartWith("touch: illegal option -- "))
.exitCode(1)
.doesNotExist("file")
.runAsTest("--no-create=1");
});
});
Loading