Skip to content

fix: honour -- as end-of-options for file-operand commands - #89

Merged
davydog187 merged 2 commits into
mainfrom
cursor/end-of-options-double-dash-850a
Aug 21, 2026
Merged

fix: honour -- as end-of-options for file-operand commands#89
davydog187 merged 2 commits into
mainfrom
cursor/end-of-options-double-dash-850a

Conversation

@davydog187

@davydog187 davydog187 commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Fixes #83

POSIX -- ends option parsing; everything after it is an operand. Some commands already did that (sort, wc, head, tac, rev). Others treated -- as a filename or rejected it as an invalid option.

The worst case was split-brain: shasum -- /f printed the correct hash and exited 1 with shasum: --: No such file or directory, so a caller checking the exit code and a caller reading stdout disagreed about whether it worked.

User-visible behavior

# before
$ shasum -- /f
<hash>  /f
shasum: --: No such file or directory    # rc=1, and the correct hash on stdout
$ cat -- /f
cat: --: No such file or directory       # rc=1
$ md5sum -- /f
md5sum: invalid option '--'              # rc=1
$ base64 -- /f
base64: invalid option '--'              # rc=1

# after (matches GNU/BSD)
$ shasum -- /f
<hash>  /f                               # rc=0, stderr empty
$ cat -- /f
hello
$ md5sum -- /f
<hash>  /f                               # rc=0
$ base64 -- /f
aGVsbG8K                                 # rc=0

-- is also how a filename beginning with a dash is passed: cat -- -f reads a file named -f. JustBash.exec/2 does not raise.

Changes

  • StdinOperand.split_end_of_options/1 is the shared -- stop that FlagParser and StdinOperand.operands/1 already implemented. Hand-rolled parsers call it once at their entry point, parse flags from the left side, and append the remainder as operands.
  • Commands that take no options (cat, touch, mv, source) drop -- rather than opening a path named --.
  • test/commands/end_of_options_test.exs enumerates cmd -- FILE for every Registry command that takes a file operand, with an explicit skip-and-reason for the rest, so a new command cannot join unclassified.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

Testing

  • Added new tests
  • All existing tests pass

Local gates:

mix compile --warnings-as-errors
mix format --check-formatted
mix credo --strict   # only the pre-existing apply/2 test fixture
mix test
# Finished in 33.4 seconds
# 2 doctests, 62 properties, 5420 tests, 0 failures (5 excluded)
mix docs --warnings-as-errors
mix dialyzer
# Total errors: 13, Skipped: 13, Unnecessary Skips: 0

Checklist

  • My code follows the style guidelines of this project
  • I have run mix format
  • I have run mix credo and addressed any issues
  • I have added tests that prove my fix is effective
  • New and existing tests pass locally with my changes
  • I have updated the CHANGELOG.md
Open in Web Open in Cursor 

cursoragent and others added 2 commits August 21, 2026 17:10
POSIX `--` ends option parsing; everything after it is an operand.
Commands that used FlagParser or StdinOperand.operands already did this;
hand-rolled parsers treated `--` as a filename or an invalid option.

The worst case was split-brain: `shasum -- /f` printed the correct hash
and still exited 1. Split once at the parser entry point and append the
remainder as operands, and enumerate `cmd -- FILE` for every Registry
command that takes a file operand so a new command cannot skip the rule.

Co-authored-by: Dave Lucia <davelucianyc@gmail.com>
Dialyzer flagged the is_integer/1 guard as never succeeding because
command arguments are binaries. The remaining Integer.parse/1 clause
is the one that was already used.

Co-authored-by: Dave Lucia <davelucianyc@gmail.com>
@davydog187
davydog187 marked this pull request as ready for review August 21, 2026 17:13
@davydog187
davydog187 merged commit b5477c3 into main Aug 21, 2026
4 checks passed
@davydog187
davydog187 deleted the cursor/end-of-options-double-dash-850a branch August 21, 2026 17:42
This was referenced Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

-- is honoured by some commands and treated as a filename or an invalid option by others

2 participants