Skip to content

Commit

Permalink
tail implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mike-ward committed Jun 1, 2024
1 parent 067262a commit 64d7977
Show file tree
Hide file tree
Showing 7 changed files with 540 additions and 0 deletions.
96 changes: 96 additions & 0 deletions src/tail/cmd_line.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import common
import flag

struct Args {
bytes i64
follow bool
lines i64
pid string
quiet bool
retry bool
sleep_interval f64
verbose bool
from_start bool
delimiter u8 = `\n`
files []string
}

fn get_args(args []string) Args {
mut fp := flag.new_flag_parser(args)

fp.application(app_name)
fp.version(common.coreutils_version())
fp.skip_executable()
fp.description('
Print the last 10 lines of each FILE to standard output.
With more than one FILE, precede each with a header giving the file name.
With no FILE, or when FILE is -, read standard input.'.trim_indent())

eol := common.eol()
wrap := eol + flag.space

bytes_arg := fp.string('bytes', `c`, '-1',
'output the last NUM bytes; or use -c +<int> to output ${wrap}' +
'starting with byte <int> of each file')

follow_arg := fp.bool('follow', `f`, false, 'output appended data as the file grows')
f_arg := fp.bool('', `F`, false, 'same as --follow=name --retry')

lines_arg := fp.string('lines', `n`, '10',
'output the last NUM lines, instead of the last 10; or us${wrap}' +
'-n +NUM to skip NUM-1 lines at the start')

pid_arg := fp.string('pid', ` `, '', 'with -f, terminate after process ID, PID dies')
quiet_arg := fp.bool('quiet', `q`, false, 'never output headers giving file names')
silent_arg := fp.bool('silent', ` `, false, 'same as --quiet')
retry_arg := fp.bool('retry', ` `, false, 'keep trying to open a file if it is inaccessible')

sleep_interval_arg := fp.float('sleep-interval', `s`, 1.0,
'with -f, sleep for approximately N seconds (default 1.0)${wrap}' +
'between iterations; with inotify and --pid=P, check${wrap}' +
'process P at least once every N seconds')

verbose_arg := fp.bool('verbose', `v`, false, 'always output headers giving file names')
zero_terminated_arg := fp.bool('zero-terminated', `z`, false, 'line delimiter is NUL, not newline')

fp.footer('
NUM may have a multiplier suffix: b 512, kB 1000, K 1024, MB
1000*1000, M 1024*1024, GB 1000*1000*1000, G 1024*1024*1024, and
so on for T, P, E, Z, Y, R, Q. Binary prefixes can be used, too:
KiB=K, MiB=M, and so on.
This implementation of TAIL follows files by name only. File
descriptors are not supported'.trim_indent())

fp.footer(common.coreutils_footer())
file_args := fp.finalize() or { exit_error(err.msg()) }
from_start := bytes_arg.starts_with('+') || lines_arg.starts_with('+')
delimiter := if zero_terminated_arg { `\0` } else { `\n` }

return Args{
bytes: string_to_i64(bytes_arg) or { exit_error(err.msg()) }
follow: follow_arg || f_arg
lines: string_to_i64(lines_arg) or { exit_error(err.msg()) }
pid: pid_arg
quiet: quiet_arg || silent_arg
retry: f_arg || retry_arg
verbose: verbose_arg
sleep_interval: sleep_interval_arg
from_start: from_start
delimiter: delimiter
files: file_args
}
}

@[noreturn]
fn exit_success(msg string) {
println(msg)
exit(0)
}

@[noreturn]
fn exit_error(msg string) {
common.exit_with_error_message(app_name, msg)
}
Empty file removed src/tail/delete.me
Empty file.
76 changes: 76 additions & 0 deletions src/tail/string_to_i64.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// convert strings like 10K to i164
const block = i64(512)

// **1
const kilo = i64(1024)
const kilobyte = i64(1000)

// **2
const mega = kilo * kilo
const megabyte = kilobyte * kilobyte

// **3
const giga = mega * kilo
const gigabyte = megabyte * kilobyte

// **4
const terra = giga * kilo
const terrabyte = gigabyte * kilobyte

// **5
const peta = terra * kilo
const petabyte = terra * kilobyte

// **6
const exa = peta * kilo
const exabyte = peta * kilo

// **7
const zetta = exa * kilo
const zettabyte = exabyte * kilobyte

fn string_to_i64(s string) ?i64 {
if s.len == 0 {
return none
}

mut index := 0
for index < s.len {
match true {
s[index].is_digit() {}
s[index] == `+` && index == 0 {}
s[index] == `-` && index == 0 {}
else { break }
}
index += 1
}

number := s[0..index].i64()
suffix := if index < s.len { s[index..] } else { 'c' }

multiplier := match suffix.to_lower() {
'b' { block }
'k' { kilo }
'kb', 'kib' { kilobyte }
'm' { mega }
'mb', 'mib' { megabyte }
'g' { giga }
'gb', 'gib' { gigabyte }
't' { terra }
'tb', 'tib' { terrabyte }
'p' { peta }
'pb', 'pib' { petabyte }
'e' { exa }
'eb', 'eib' { exabyte }
// oddball formats found in __xstrtol source
'c' { 1 }
'w' { 2 }
else { return none }
}

result := number * multiplier
if result == 0 && number != 0 {
return none
}
return result
}
46 changes: 46 additions & 0 deletions src/tail/string_to_i64_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module main

fn test_string_to_i64_conversions() {
assert string_to_i64('1')! == 1
assert string_to_i64('+1')! == 1
assert string_to_i64('-1')! == -1

assert string_to_i64('2b')! == 2 * block

assert string_to_i64('11k')! == 11 * kilo
assert string_to_i64('12K')! == 12 * kilo
assert string_to_i64('13KB')! == 13 * kilobyte
assert string_to_i64('14KiB')! == 14 * kilobyte

assert string_to_i64('15M')! == 15 * mega
assert string_to_i64('16MB')! == 16 * megabyte
assert string_to_i64('17mib')! == 17 * megabyte

assert string_to_i64('18T')! == 18 * terra
assert string_to_i64('18TB')! == 18 * terrabyte
assert string_to_i64('18TiB')! == 18 * terrabyte

assert string_to_i64('10P')! == 10 * peta
assert string_to_i64('10PB')! == 10 * petabyte
assert string_to_i64('10PiB')! == 10 * petabyte

assert string_to_i64('5E')! == 5 * exa
assert string_to_i64('5EB')! == 5 * exabyte
assert string_to_i64('5EiB')! == 5 * exabyte

overflow_r := string_to_i64('5R') or { -1 }
assert overflow_r == -1

overflow_q := string_to_i64('5Q') or { -1 }
assert overflow_q == -1

// invalid checks
t0 := string_to_i64('') or { -1 }
assert t0 == -1

t1 := string_to_i64('1x') or { -1 }
assert t1 == -1

t2 := string_to_i64('++1') or { -1 }
assert t2 == -1
}
Loading

0 comments on commit 64d7977

Please sign in to comment.