Skip to content

Commit

Permalink
Merge branch 'main' into igrekus/implement-sum
Browse files Browse the repository at this point in the history
  • Loading branch information
igrekus committed Jan 29, 2024
2 parents 0852435 + 091c70a commit 90c810d
Show file tree
Hide file tree
Showing 37 changed files with 2,796 additions and 40 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Contributions are welcome!
Please only contribute versions of the original utilities written in V.
Contributions written in other languages will likely be rejected.

## Completed (45/109)
## Completed (48/109)

| Done | Cmd | Descripton |
| :-----: | --------- | ------------------------------------------------ |
Expand All @@ -55,7 +55,7 @@ Contributions written in other languages will likely be rejected.
| | chmod | Change access permissions |
| | chown | Change file owner and group |
| | chroot | Run a command with a different root directory |
| | cksum | Print CRC checksum and byte counts |
| ✓ | cksum | Print CRC checksum and byte counts |
| | comm | Compare two sorted files line by line |
| | coreutils | Multi-call program |
| ✓ | cp | Copy files and directories |
Expand Down Expand Up @@ -142,8 +142,8 @@ Contributions written in other languages will likely be rejected.
| | tty | Print file name of terminal on standard input |
| ✓ | uname | Print system information |
| | unexpand | Convert spaces to tabs |
| | uniq | Uniquify files |
| | unlink | Remove files via the unlink syscall |
| ✓ | uniq | Uniquify files |
| ✓ | unlink | Remove files via the unlink syscall |
| ✓ | uptime | Print system uptime and load |
| | users | Print login names of users currently logged in |
| | vdir | Verbosely list directory contents |
Expand Down
38 changes: 38 additions & 0 deletions common/common.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ import flag

pub const version = '0.0.1'

pub struct CoreutilInfo {
pub:
name string
description string
}

pub struct CoreutilExitDetail {
message string
mut:
return_code int = 1
show_help_advice bool // defaults to false
}

// coreutils_version returns formatted coreutils tool version
pub fn coreutils_version() string {
return '(V coreutils) ${common.version}'
Expand Down Expand Up @@ -46,3 +59,28 @@ pub fn exit_with_error_message(tool_name string, error string) {
eprintln("Try '${tool_name} --help' for more information.")
exit(1)
}

// A common way to exit with a custom error code (default: 1)
// and the ability to direct the user to help (default: false)
// to make it easier to match the output of the GNU coreutils
@[noreturn]
pub fn (app CoreutilInfo) quit(detail CoreutilExitDetail) {
eprintln('${app.name}: ${detail.message}')
if detail.show_help_advice {
eprintln("Try '${app.name} --help' for more information.")
}
exit(detail.return_code)
}

// flag_parser returns a flag.FlagParser, with the common
// options already set, reducing the boilerplate code in
// each individual utility.
pub fn (app CoreutilInfo) make_flag_parser(args []string) &flag.FlagParser {
mut fp := flag.new_flag_parser(args)
fp.version(coreutils_version())
fp.footer(coreutils_footer())
fp.skip_executable()
fp.application(app.name)
fp.description(app.description)
return fp
}
5 changes: 5 additions & 0 deletions src/arch/arch_test.v
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import common.testing
import os

const util = 'arch'

Expand All @@ -12,6 +13,10 @@ const executable_under_test = testing.prepare_executable(util)

const cmd = testing.new_paired_command(platform_util, executable_under_test)

fn testsuite_begin() {
os.chdir(testing.temp_folder)!
}

fn test_help_and_version() {
cmd.ensure_help_and_version_options_work()!
}
Expand Down
4 changes: 4 additions & 0 deletions src/base64/base64_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ const executable_under_test = testing.prepare_executable(util)

const cmd = testing.new_paired_command(platform_util, executable_under_test)

fn testsuite_begin() {
os.chdir(testing.temp_folder)!
}

fn test_help_and_version() {
cmd.ensure_help_and_version_options_work()!
}
Expand Down
126 changes: 126 additions & 0 deletions src/cksum/cksum.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
module main

import os
import common
import io
import arrays

const app_name = 'cksum'
const app_description = 'Print CRC checksum and byte counts of each FILE.'
const buffer_length = 128 * 1024

struct Args {
fnames []string
}

fn swap_32(x u32) u32 {
return ((x & 0xff000000) >> 24) | ((x & 0x00ff0000) >> 8) | ((x & 0x0000ff00) << 8) | ((x & 0x000000ff) << 24)
}

fn calc_sums(args Args) {
mut files := args.fnames.clone()

// read from stdin if no files supplied
if files.len < 1 {
files = ['-']
}

mut f := os.File{}
mut buf := []u8{len: buffer_length, cap: buffer_length}

for file in files {
if file == '-' {
f = os.stdin()
} else {
f = os.open(file) or {
eprintln('cksum: ${file}: No such file or directory')
exit(1)
}
defer {
f.close()
}
}

mut rd := io.new_buffered_reader(io.BufferedReaderConfig{ reader: f, cap: buffer_length })
mut crc := u64(0)
mut total_length := u64(0)

for {
res := u64(rd.read(mut buf) or { break })

chunks := res / 8
for outer := 0; outer < chunks; outer++ {
chunk := buf[outer * 8..outer * 8 + 8].clone()
first := four_bytes_to_int(chunk[..4])
mut second := four_bytes_to_int(chunk[4..])

crc ^= swap_32(first)
second = swap_32(second)
// println('${crc} ${second}')

crc = crctab[7][(crc >> 24) & 0xFF] ^ crctab[6][(crc >> 16) & 0xFF] ^ crctab[5][(crc >> 8) & 0xFF] ^ crctab[4][crc & 0xFF] ^ crctab[3][(second >> 24) & 0xFF] ^ crctab[2][(second >> 16) & 0xFF] ^ crctab[1][(second >> 8) & 0xFF] ^ crctab[0][second & 0xFF]
}

remaining_len := res - chunks * 8
if remaining_len > 0 {
rest := buf[8 * chunks..].clone()
crc = cksum_slice8(rest, crc, remaining_len)
}
total_length = u64(rd.total_read)
}

mut len_counter := total_length
for ; len_counter; len_counter >>= 8 {
crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ len_counter) & 0xFF]
}
crc = ~crc & 0xffff_ffff

file_str := match file {
'-' { '' }
else { file }
}
println('${crc} ${total_length} ${file_str}')
}
}

fn cksum_slice8(buf []u8, crc_in u64, remaining_len u64) u64 {
mut crc_tmp := crc_in

for i := 0; i < remaining_len; i++ {
cp := buf[i]
index := ((crc_tmp >> 24) ^ cp) & 0xFF
tab_value := crctab[0][index]
crc_shift := crc_tmp << 8
crc_tmp = crc_shift ^ tab_value
}

return crc_tmp
}

fn id[T](x T) T {
return x
}

fn four_bytes_to_int(bytes []u8) u32 {
// emulates original evil type punning
// TODO this is *damn* slow -- rewrite via evil bit hacking
mut tmp_bytes := []string{}
for c in bytes.reverse() {
tmp_bytes << c.hex()
}
return u32(arrays.join_to_string[string](tmp_bytes, '', id[string])
.parse_uint(16, 32) or { panic(err) })
}

fn parse_args() Args {
mut fp := common.flag_parser(os.args)
fp.application(app_name)
fp.description(app_description)

fnames := fp.remaining_parameters()
return Args{fnames}
}

fn main() {
calc_sums(parse_args())
}
66 changes: 66 additions & 0 deletions src/cksum/cksum_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import os
import common.testing

const eol = testing.output_eol()
const util = 'cksum'

const platform_util = $if !windows {
util
} $else {
'coreutils ${util}'
}

const executable_under_test = testing.prepare_executable(util)

const cmd = testing.new_paired_command(platform_util, executable_under_test)

const temp_dir = testing.temp_folder
const test1_txt_path = os.join_path(temp_dir, 'test1.txt')
const test2_txt_path = os.join_path(temp_dir, 'test2.txt')
const test3_txt_path = os.join_path(temp_dir, 'test3.txt')
const dummy = os.join_path(temp_dir, 'dummy')
const long_over_16k = os.join_path(temp_dir, 'long_over_16k')
const long_under_16k = os.join_path(temp_dir, 'long_under_16k')

fn test_help_and_version() {
cmd.ensure_help_and_version_options_work()!
}

fn testsuite_begin() {
os.chdir(testing.temp_folder)!
os.write_file(test1_txt_path, 'Hello World!\nHow are you?')!
os.write_file(test2_txt_path, 'a'.repeat(128 * 1024 + 5))!
}

fn testsuite_end() {
os.rm(test1_txt_path)!
os.rm(test2_txt_path)!
}

fn test_stdin() {
res := os.execute('cat ${test1_txt_path} | ${executable_under_test}')

assert res.exit_code == 0
assert res.output.trim_space() == '365965416 25'
}

fn test_file_not_exist() {
res := os.execute('${executable_under_test} abcd')

assert res.exit_code == 1
assert res.output.trim_space() == 'cksum: abcd: No such file or directory'
}

fn test_one_file() {
res := os.execute('${executable_under_test} ${test1_txt_path}')

assert res.exit_code == 0
assert res.output == '365965416 25 ${test1_txt_path}${eol}'
}

fn test_several_files() {
res := os.execute('${executable_under_test} ${test1_txt_path} ${test2_txt_path}')

assert res.exit_code == 0
assert res.output == '365965416 25 ${test1_txt_path}${eol}1338884673 131077 ${test2_txt_path}${eol}'
}
Loading

0 comments on commit 90c810d

Please sign in to comment.