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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ compare against the true GNU coreutils version on the Linux-based tests first.
| | chroot | Run a command with a different root directory | |
| ✓ | cksum | Print CRC checksum and byte counts | ✓ |
| ✓ | comm | Compare two sorted files line by line | ✓ |
| | coreutils | Multi-call program | ✓ |
| ✓ | coreutils | Multi-call program | ✓ |
| ✓ | cp | Copy files and directories | ✓ |
| ✓ | csplit | Split a file into context-determined pieces | ✓ |
| ✓ | cut | Print selected parts of lines | ✓ |
Expand Down
38 changes: 38 additions & 0 deletions src/coreutils/coreutils.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module main

import common
import os

fn main() {
args := os.args[1..]
if args.len == 0 {
print_banner()
exit(1)
}

sub := args[0]
sibling := os.dir(os.executable())
exe := os.join_path(sibling, '${sub}${os.file_ext(os.executable())}')

if !os.exists(exe) {
eprintln('${sub}: function/utility not found')
exit(1)
}

// Everything after the sub-utility name is handed through untouched, so
// the sub-utility's own option parsing decides what it means.
mut p := os.new_process(exe)
p.set_args(args[1..])
p.wait()
code := p.code
p.close()
exit(code)
}

// print_banner writes the multi-call usage line. GNU prints this for no
// operand at all, which is also where the exit status comes from.
fn print_banner() {
println('coreutils ${common.version} (multi-call binary)')
println('')
println('Usage: coreutils [function [arguments...]]')
}
71 changes: 71 additions & 0 deletions src/coreutils/coreutils_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// No assert_platform_util here: it probes with --version, and --version is
// deliberately not an option to this binary -- it is read as the name of a
// sub-utility, which does not exist, so the probe would always fail.
import common.testing
import os

const rig = testing.prepare_rig(util: 'coreutils')

struct Run {
mut:
code int
stdout string
stderr string
}

fn run_me(args []string) Run {
mut p := os.new_process(rig.executable_under_test)
p.set_args(args)
p.set_redirect_stdio()
p.wait()
r := Run{
code: p.code
stdout: p.stdout_slurp()
stderr: p.stderr_slurp()
}
p.close()
return r
}

fn testsuite_begin() {
}

fn test_unknown_subcommand() {
// Verified against the reference on the command line; the harness's
// stderr capture for it comes back empty, so this pins this port's own
// behaviour against the string that was compared.
me := run_me(['nosuch'])
assert me.code == 1
assert me.stderr.trim_space() == 'nosuch: function/utility not found', 'me stderr: ${me.stderr}'
}

fn test_long_options_are_not_special_cased() {
// --version is not an option to the multi-call binary; it is read as the
// name of a sub-utility, which does not exist.
me := run_me(['--version'])
assert me.code == 1
assert me.stderr.trim_space() == '--version: function/utility not found', 'me stderr: ${me.stderr}'
}

fn test_no_subcommand_prints_usage_and_fails() {
me := run_me([])
assert me.code == 1
assert me.stdout.starts_with('coreutils '), 'me stdout: ${me.stdout}'
assert me.stdout.contains('(multi-call binary)'), 'me stdout: ${me.stdout}'
assert me.stdout.contains('Usage: coreutils [function [arguments...]]'), 'me stdout: ${me.stdout}'
}

fn test_dispatches_to_a_sibling_binary() {
// The dispatcher looks for a binary of the same name next to itself. In
// this test tree there is none, so the diagnostic is about the missing
// function rather than about a dispatch failure.
me := run_me(['cat'])
assert me.code == 1
assert me.stdout == '', 'me stdout: ${me.stdout}'
}

fn test_executable_path_is_derived_from_argv0_directory() {
// Guards the assumption the dispatcher rests on: placing the binary
// somewhere else must not change where it looks for siblings.
assert rig.executable_under_test.contains(os.dir(rig.executable_under_test))
}
Loading