///|
fn help_message() -> String {
let message =
#|Usage: comm [-123] FILE1 FILE2
#|
#|Compare two sorted files line by line, producing three columns:
#|lines only in FILE1, lines only in FILE2, and lines in both.
#|
#|Options:
#| -1 Suppress column 1 (lines unique to FILE1).
#| -2 Suppress column 2 (lines unique to FILE2).
#| -3 Suppress column 3 (lines in both files).
#| -h, --help Show this help message.
#|
#|Use '-' as a file name to read stdin.
message
}
///|
async fn write_line(prefix : String, line : Bytes) -> Unit {
if prefix != "" {
@stdio.stdout.write(prefix)
}
@stdio.stdout.write(line)
@stdio.stdout.write("\n")
}
///|
async fn merge_readers(
reader1 : &@io.Reader,
reader2 : &@io.Reader,
hide1 : Bool,
hide2 : Bool,
hide3 : Bool,
) -> Unit {
let scanner1 = @stream.LineScanner::new(reader1)
let scanner2 = @stream.LineScanner::new(reader2)
let mut line1 = scanner1.next()
let mut line2 = scanner2.next()
let col2_prefix = if hide1 { "" } else { "\t" }
let col3_prefix = (if hide1 { "" } else { "\t" }) +
(if hide2 { "" } else { "\t" })
while line1 is Some(left) && line2 is Some(right) {
let order = left.data[:].lexical_compare(right.data[:])
if order < 0 {
if !hide1 {
write_line("", left.data)
}
line1 = scanner1.next()
} else if order > 0 {
if !hide2 {
write_line(col2_prefix, right.data)
}
line2 = scanner2.next()
} else {
if !hide3 {
write_line(col3_prefix, left.data)
}
line1 = scanner1.next()
line2 = scanner2.next()
}
}
while line1 is Some(left) {
if !hide1 {
write_line("", left.data)
}
line1 = scanner1.next()
}
while line2 is Some(right) {
if !hide2 {
write_line(col2_prefix, right.data)
}
line2 = scanner2.next()
}
}
///|
async fn main {
let args = @env.args()[1:]
let parsed = @cli.parse(args, [
@cli.flag("suppress-1", short='1'),
@cli.flag("suppress-2", short='2'),
@cli.flag("suppress-3", short='3'),
@cli.flag("help", short='h'),
]) catch {
@cli.CliError(option~, message~, ..) => {
@stdio.stderr.write("comm: \{message}: '\{option}'\n")
@sys.exit(2)
return
}
}
if parsed.contains("help") {
@stdio.stdout.write(help_message() + "\n")
return
}
let hide1 = parsed.contains("suppress-1")
let hide2 = parsed.contains("suppress-2")
let hide3 = parsed.contains("suppress-3")
let files = parsed.operands
if files.length() != 2 {
@stdio.stderr.write(
"comm: expected exactly two files\n\n" + help_message() + "\n",
)
@sys.exit(2)
return
}
if files[0] == "-" && files[1] == "-" {
@stdio.stderr.write("comm: both inputs cannot be standard input\n")
@sys.exit(2)
return
}
try {
if files[0] == "-" {
let file2 = @fs.open(files[1], mode=ReadOnly)
defer file2.close()
merge_readers(@stdio.stdin, file2, hide1, hide2, hide3)
} else if files[1] == "-" {
let file1 = @fs.open(files[0], mode=ReadOnly)
defer file1.close()
merge_readers(file1, @stdio.stdin, hide1, hide2, hide3)
} else {
let file1 = @fs.open(files[0], mode=ReadOnly)
defer file1.close()
let file2 = @fs.open(files[1], mode=ReadOnly)
defer file2.close()
merge_readers(file1, file2, hide1, hide2, hide3)
}
} catch {
err => {
@stdio.stderr.write("comm: \{err}\n")
@sys.exit(1)
return
}
}
}