module main
import (
std.fs
std.io
std.os
)
let Counts = type struct {
lines: usz,
words: usz,
bytes: usz,
}
let Options = type struct {
lines: bool,
words: bool,
bytes: bool,
}
let is_whitespace(byte: u8): bool =
byte == ' ' || byte == '\t' || byte == '\n' ||
byte == '\r' || byte == '\v' || byte == '\f'
let count(reader: *mut dyn std.io.Reader): Result(Counts, Error) {
let mut result: Counts = .{ lines=0, words=0, bytes=0 }
let mut in_word = false
let mut buffer: [4096]u8 = ---
for true {
let read = match reader.read(buffer[:]) {
.Ok(value) => value,
.Err(error) => { return .Err(error) },
}
if read == 0 { break }
result.bytes += read
for byte in buffer[0:read] {
if byte == '\n' { result.lines += 1 }
if is_whitespace(byte) {
in_word = false
} else if !in_word {
result.words += 1
in_word = true
}
}
}
return .Ok(result)
}
let add(total: *mut Counts, counts: Counts): void {
total.lines += counts.lines
total.words += counts.words
total.bytes += counts.bytes
}
let digit_count(value: usz): usz {
let mut digits: usz = 1
let mut remaining = value
for remaining >= 10 {
remaining /= 10
digits += 1
}
return digits
}
let print_count(value: usz, width: usz, separator: bool): void {
if separator { std.io.print(" ") }
for _ in digit_count(value)..width { std.io.print(" ") }
std.io.print("{}", value)
}
let print_counts(counts: Counts, options: Options, name: str, named: bool, width: usz): void {
let mut printed = false
if options.lines {
print_count(counts.lines, width, printed)
printed = true
}
if options.words {
print_count(counts.words, width, printed)
printed = true
}
if options.bytes {
print_count(counts.bytes, width, printed)
}
if named { std.io.print(" {}", name) }
std.io.println("")
}
let output_width(first_file: usz): usz {
if first_file == @len(std.os.args) { return 7 }
let mut total_size: usz = 0
for index in first_file..@len(std.os.args) {
let path = std.os.args[index]
if path.eq("-") { return 7 }
let size = std.fs.file_size(path)
if size.is_ok() { total_size += size.unwrap() }
}
return digit_count(total_size)
}
let usage(): void {
std.io.eprintln("usage: wc [-clw] [--bytes] [--lines] [--words] [file ...]")
}
let parse_short_options(argument: str, options: *mut Options): bool {
if @len(argument) < 2 || argument[0] != '-' { return false }
for index in 1..@len(argument) {
let option = argument[index]
if option == 'c' {
options.bytes = true
} else if option == 'l' {
options.lines = true
} else if option == 'w' {
options.words = true
} else {
return false
}
}
return true
}
let main() {
let mut options: Options = .{ lines=false, words=false, bytes=false }
let mut first_file: usz = 1
let mut parsing_options = true
for first_file < @len(std.os.args) {
let argument = std.os.args[first_file]
if !parsing_options || argument.eq("-") || @len(argument) == 0 || argument[0] != '-' {
break
}
if argument.eq("--") {
parsing_options = false
first_file += 1
break
}
if argument.eq("--bytes") {
options.bytes = true
} else if argument.eq("--lines") {
options.lines = true
} else if argument.eq("--words") {
options.words = true
} else if argument.eq("--help") {
usage()
return
} else if !parse_short_options(argument, options.&mut) {
std.io.eprintln("wc: unrecognized option '{}'", argument)
usage()
std.os.exit(1)
}
first_file += 1
}
if !options.lines && !options.words && !options.bytes {
options.lines = true
options.words = true
options.bytes = true
}
let width = output_width(first_file)
if first_file == @len(std.os.args) {
let result = count(std.io.stdin.&mut.(*mut dyn std.io.Reader))
if result.is_err() {
std.io.eprintln("wc: stdin: {}", result.unwrap_err().message())
std.os.exit(1)
}
print_counts(result.unwrap(), options, "", false, width)
return
}
let file_count = @len(std.os.args) - first_file
let mut total: Counts = .{ lines=0, words=0, bytes=0 }
let mut failed = false
for index in first_file..@len(std.os.args) {
let path = std.os.args[index]
if path.eq("-") {
let result = count(std.io.stdin.&mut.(*mut dyn std.io.Reader))
if result.is_err() {
std.io.eprintln("wc: stdin: {}", result.unwrap_err().message())
failed = true
continue
}
let counts = result.unwrap()
print_counts(counts, options, path, true, width)
add(total.&mut, counts)
continue
}
let open_result = std.io.FileReader.open(path)
if open_result.is_err() {
std.io.eprintln("wc: {}: {}", path, open_result.unwrap_err().message())
failed = true
continue
}
let mut file = open_result.unwrap()
let read_result = count(file.&mut.(*mut dyn std.io.Reader))
let close_result = file.close()
if read_result.is_err() {
std.io.eprintln("wc: {}: {}", path, read_result.unwrap_err().message())
failed = true
continue
}
if close_result.is_err() {
std.io.eprintln("wc: {}: {}", path, close_result.unwrap_err().message())
failed = true
}
let counts = read_result.unwrap()
print_counts(counts, options, path, true, width)
add(total.&mut, counts)
}
if file_count > 1 { print_counts(total, options, "total", true, width) }
if failed { std.os.exit(1) }
}