-
Notifications
You must be signed in to change notification settings - Fork 255
Expand file tree
/
Copy pathconsole.rs
More file actions
132 lines (117 loc) · 4.61 KB
/
Copy pathconsole.rs
File metadata and controls
132 lines (117 loc) · 4.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use std::io::Read as _;
use std::io::Write as _;
use anyhow::Context as _;
use crate::config::NewlineMode;
use crate::config::OutputMode;
use crate::config::OutputMode::*;
pub fn open(
port: &std::path::Path,
baudrate: u32,
output_mode: OutputMode,
newline_mode: NewlineMode,
space_after: Option<u8>,
) -> anyhow::Result<()> {
let mut rx = serialport::new(port.to_string_lossy(), baudrate)
.timeout(std::time::Duration::from_secs(2))
.open_native()
.with_context(|| format!("failed to open serial port `{}`", port.display()))?;
let mut tx = rx.try_clone_native()?;
let mut stdin = std::io::stdin();
let mut stdout = std::io::stdout();
// Set a CTRL+C handler to terminate cleanly instead of with an error.
ctrlc::set_handler(move || {
eprintln!();
eprintln!("Exiting.");
std::process::exit(0);
})
.context("failed setting a CTRL+C handler")?;
let mut byte_count = 0;
// Spawn a thread for the receiving end because stdio is not portably non-blocking...
let receiver = std::thread::spawn(move || -> anyhow::Result<()> {
loop {
#[cfg(not(target_os = "windows"))]
let mut buf = [0u8; 4098];
// Use buffer size 1 for windows because it blocks on rx.read until the buffer is full
#[cfg(target_os = "windows")]
let mut buf = [0u8; 1];
match rx.read(&mut buf) {
Ok(count) => {
#[cfg(target_os = "windows")]
{
// On windows, we must ensure that we are not sending anything outside of the
// ASCII range.
for byte in &mut buf[..count] {
if *byte & 0x80 != 0 {
*byte = '?'.try_into().unwrap();
}
}
}
if output_mode == Ascii {
stdout.write_all(&buf[..count])?;
} else {
for byte in &buf[..count] {
byte_count += 1;
match output_mode {
Ascii => unreachable!(),
Hex => write!(stdout, "{:02x} ", byte)?,
Dec => write!(stdout, "{:03} ", byte)?,
Bin => write!(stdout, "{:08b} ", byte)?,
}
if let Some(space_after) = space_after {
if byte_count % space_after == 0 {
write!(stdout, " ")?;
}
}
match newline_mode {
NewlineMode::On(newline_on) => {
if *byte == newline_on {
writeln!(stdout)?
}
}
NewlineMode::After(newline_after) => {
if byte_count % newline_after == 0 {
writeln!(stdout)?;
}
}
NewlineMode::Off => {}
}
}
}
stdout.flush()?;
}
Err(e) => {
assert!(e.kind() == std::io::ErrorKind::TimedOut);
}
}
}
});
// Spawn a thread for the sending end because stdio is not portably non-blocking...
let sender = std::thread::spawn(move || -> anyhow::Result<()> {
loop {
let mut buf = [0u8; 4098];
let count = stdin.read(&mut buf)?;
tx.write_all(&buf[..count])?;
tx.flush()?;
}
});
loop {
if sender.is_finished() {
sender
.join()
.unwrap()
.context("error while sending data to target")?;
break;
}
if receiver.is_finished() {
receiver
.join()
.unwrap()
.context("error while receiving data from target")?;
break;
}
// We don't have a portable select so instead we poll our two threads every 100ms. Not
// pretty but at least this should work absolutely everywhere...
std::thread::sleep(std::time::Duration::from_millis(100));
}
Ok(())
}