1use std::io;
2
3pub fn baud_to_num(speed: libc::speed_t) -> u32 {
5 match speed {
6 libc::B0 => 0,
7 libc::B50 => 50,
8 libc::B75 => 75,
9 libc::B110 => 110,
10 libc::B134 => 134,
11 libc::B150 => 150,
12 libc::B200 => 200,
13 libc::B300 => 300,
14 libc::B600 => 600,
15 libc::B1200 => 1200,
16 libc::B1800 => 1800,
17 libc::B2400 => 2400,
18 libc::B4800 => 4800,
19 libc::B9600 => 9600,
20 libc::B19200 => 19200,
21 libc::B38400 => 38400,
22 libc::B57600 => 57600,
23 libc::B115200 => 115200,
24 libc::B230400 => 230400,
25 _ => 0,
26 }
27}
28
29pub fn num_to_baud(num: u32) -> Option<libc::speed_t> {
31 match num {
32 0 => Some(libc::B0),
33 50 => Some(libc::B50),
34 75 => Some(libc::B75),
35 110 => Some(libc::B110),
36 134 => Some(libc::B134),
37 150 => Some(libc::B150),
38 200 => Some(libc::B200),
39 300 => Some(libc::B300),
40 600 => Some(libc::B600),
41 1200 => Some(libc::B1200),
42 1800 => Some(libc::B1800),
43 2400 => Some(libc::B2400),
44 4800 => Some(libc::B4800),
45 9600 => Some(libc::B9600),
46 19200 => Some(libc::B19200),
47 38400 => Some(libc::B38400),
48 57600 => Some(libc::B57600),
49 115200 => Some(libc::B115200),
50 230400 => Some(libc::B230400),
51 _ => None,
52 }
53}
54
55pub fn get_termios(fd: i32) -> io::Result<libc::termios> {
57 let mut termios: libc::termios = unsafe { std::mem::zeroed() };
58 if unsafe { libc::tcgetattr(fd, &mut termios) } != 0 {
59 return Err(io::Error::last_os_error());
60 }
61 Ok(termios)
62}
63
64pub fn set_termios(fd: i32, termios: &libc::termios) -> io::Result<()> {
66 if unsafe { libc::tcsetattr(fd, libc::TCSADRAIN, termios) } != 0 {
67 return Err(io::Error::last_os_error());
68 }
69 Ok(())
70}
71
72pub fn get_winsize(fd: i32) -> io::Result<libc::winsize> {
74 let mut ws: libc::winsize = unsafe { std::mem::zeroed() };
75 if unsafe { libc::ioctl(fd, libc::TIOCGWINSZ, &mut ws) } != 0 {
76 return Err(io::Error::last_os_error());
77 }
78 Ok(ws)
79}
80
81pub fn print_size(fd: i32) -> io::Result<()> {
83 let ws = get_winsize(fd)?;
84 println!("{} {}", ws.ws_row, ws.ws_col);
85 Ok(())
86}
87
88pub fn print_speed(termios: &libc::termios) {
90 let ispeed = unsafe { libc::cfgetispeed(termios) };
91 let ospeed = unsafe { libc::cfgetospeed(termios) };
92 if ispeed == ospeed {
93 println!("{}", baud_to_num(ospeed));
94 } else {
95 println!("{} {}", baud_to_num(ispeed), baud_to_num(ospeed));
96 }
97}
98
99pub fn format_cc(c: libc::cc_t) -> String {
101 if c == 0 {
102 "<undef>".to_string()
103 } else if c == 0x7f {
104 "^?".to_string()
105 } else if c < 0x20 {
106 format!("^{}", (c + 0x40) as char)
107 } else {
108 format!("{}", c as char)
109 }
110}
111
112#[cfg(target_os = "linux")]
114const SPECIAL_CHARS_ALL: &[(&str, usize)] = &[
115 ("intr", libc::VINTR as usize),
116 ("quit", libc::VQUIT as usize),
117 ("erase", libc::VERASE as usize),
118 ("kill", libc::VKILL as usize),
119 ("eof", libc::VEOF as usize),
120 ("eol", libc::VEOL as usize),
121 ("eol2", libc::VEOL2 as usize),
122 ("swtch", libc::VSWTC as usize),
123 ("start", libc::VSTART as usize),
124 ("stop", libc::VSTOP as usize),
125 ("susp", libc::VSUSP as usize),
126 ("rprnt", libc::VREPRINT as usize),
127 ("werase", libc::VWERASE as usize),
128 ("lnext", libc::VLNEXT as usize),
129 ("discard", libc::VDISCARD as usize),
130 ("min", libc::VMIN as usize),
131 ("time", libc::VTIME as usize),
132];
133
134#[cfg(not(target_os = "linux"))]
135const SPECIAL_CHARS_ALL: &[(&str, usize)] = &[
136 ("intr", libc::VINTR as usize),
137 ("quit", libc::VQUIT as usize),
138 ("erase", libc::VERASE as usize),
139 ("kill", libc::VKILL as usize),
140 ("eof", libc::VEOF as usize),
141 ("eol", libc::VEOL as usize),
142 ("eol2", libc::VEOL2 as usize),
143 ("start", libc::VSTART as usize),
144 ("stop", libc::VSTOP as usize),
145 ("susp", libc::VSUSP as usize),
146 ("rprnt", libc::VREPRINT as usize),
147 ("werase", libc::VWERASE as usize),
148 ("lnext", libc::VLNEXT as usize),
149 ("discard", libc::VDISCARD as usize),
150 ("min", libc::VMIN as usize),
151 ("time", libc::VTIME as usize),
152];
153
154#[cfg(target_os = "linux")]
156const INPUT_FLAGS: &[(&str, libc::tcflag_t)] = &[
157 ("ignbrk", libc::IGNBRK),
158 ("brkint", libc::BRKINT),
159 ("ignpar", libc::IGNPAR),
160 ("parmrk", libc::PARMRK),
161 ("inpck", libc::INPCK),
162 ("istrip", libc::ISTRIP),
163 ("inlcr", libc::INLCR),
164 ("igncr", libc::IGNCR),
165 ("icrnl", libc::ICRNL),
166 ("ixon", libc::IXON),
167 ("ixoff", libc::IXOFF),
168 ("iuclc", libc::IUCLC),
169 ("ixany", libc::IXANY),
170 ("imaxbel", libc::IMAXBEL),
171 ("iutf8", libc::IUTF8),
172];
173
174#[cfg(not(target_os = "linux"))]
175const INPUT_FLAGS: &[(&str, libc::tcflag_t)] = &[
176 ("ignbrk", libc::IGNBRK),
177 ("brkint", libc::BRKINT),
178 ("ignpar", libc::IGNPAR),
179 ("parmrk", libc::PARMRK),
180 ("inpck", libc::INPCK),
181 ("istrip", libc::ISTRIP),
182 ("inlcr", libc::INLCR),
183 ("igncr", libc::IGNCR),
184 ("icrnl", libc::ICRNL),
185 ("ixon", libc::IXON),
186 ("ixany", libc::IXANY),
187 ("ixoff", libc::IXOFF),
188 ("imaxbel", libc::IMAXBEL),
189];
190
191#[cfg(target_os = "linux")]
193const OUTPUT_FLAGS: &[(&str, libc::tcflag_t)] = &[
194 ("opost", libc::OPOST),
195 ("olcuc", libc::OLCUC),
196 ("ocrnl", libc::OCRNL),
197 ("onlcr", libc::ONLCR),
198 ("onocr", libc::ONOCR),
199 ("onlret", libc::ONLRET),
200 ("ofill", libc::OFILL),
201 ("ofdel", libc::OFDEL),
202];
203
204#[cfg(not(target_os = "linux"))]
205const OUTPUT_FLAGS: &[(&str, libc::tcflag_t)] = &[
206 ("opost", libc::OPOST),
207 ("onlcr", libc::ONLCR),
208 ("ocrnl", libc::OCRNL),
209 ("onocr", libc::ONOCR),
210 ("onlret", libc::ONLRET),
211 ("ofill", libc::OFILL),
212 ("ofdel", libc::OFDEL),
213];
214
215#[cfg(target_os = "linux")]
217const OUTPUT_DELAY_FLAGS: &[(&str, libc::tcflag_t, libc::tcflag_t)] = &[
218 ("nl0", libc::NL0, libc::NLDLY),
219 ("nl1", libc::NL1, libc::NLDLY),
220 ("cr0", libc::CR0, libc::CRDLY),
221 ("cr1", libc::CR1, libc::CRDLY),
222 ("cr2", libc::CR2, libc::CRDLY),
223 ("cr3", libc::CR3, libc::CRDLY),
224 ("tab0", libc::TAB0, libc::TABDLY),
225 ("tab1", libc::TAB1, libc::TABDLY),
226 ("tab2", libc::TAB2, libc::TABDLY),
227 ("tab3", libc::TAB3, libc::TABDLY),
228 ("bs0", libc::BS0, libc::BSDLY),
229 ("bs1", libc::BS1, libc::BSDLY),
230 ("vt0", libc::VT0, libc::VTDLY),
231 ("vt1", libc::VT1, libc::VTDLY),
232 ("ff0", libc::FF0, libc::FFDLY),
233 ("ff1", libc::FF1, libc::FFDLY),
234];
235
236#[cfg(not(target_os = "linux"))]
237const OUTPUT_DELAY_FLAGS: &[(&str, libc::tcflag_t, libc::tcflag_t)] = &[];
238
239const CONTROL_FLAGS: &[(&str, libc::tcflag_t)] = &[
241 ("parenb", libc::PARENB),
242 ("parodd", libc::PARODD),
243 ("hupcl", libc::HUPCL),
244 ("cstopb", libc::CSTOPB),
245 ("cread", libc::CREAD),
246 ("clocal", libc::CLOCAL),
247];
248
249#[cfg(target_os = "linux")]
251const CONTROL_FLAGS_LINUX: &[(&str, libc::tcflag_t)] =
252 &[("cmspar", libc::CMSPAR), ("crtscts", libc::CRTSCTS)];
253
254#[cfg(not(target_os = "linux"))]
255const CONTROL_FLAGS_LINUX: &[(&str, libc::tcflag_t)] = &[];
256
257#[cfg(target_os = "linux")]
259const LOCAL_FLAGS: &[(&str, libc::tcflag_t)] = &[
260 ("isig", libc::ISIG),
261 ("icanon", libc::ICANON),
262 ("iexten", libc::IEXTEN),
263 ("echo", libc::ECHO),
264 ("echoe", libc::ECHOE),
265 ("echok", libc::ECHOK),
266 ("echonl", libc::ECHONL),
267 ("noflsh", libc::NOFLSH),
268 ("xcase", libc::XCASE),
269 ("tostop", libc::TOSTOP),
270 ("echoprt", libc::ECHOPRT),
271 ("echoctl", libc::ECHOCTL),
272 ("echoke", libc::ECHOKE),
273 ("flusho", libc::FLUSHO),
274 ("extproc", libc::EXTPROC),
275];
276
277#[cfg(not(target_os = "linux"))]
278const LOCAL_FLAGS: &[(&str, libc::tcflag_t)] = &[
279 ("isig", libc::ISIG),
280 ("icanon", libc::ICANON),
281 ("iexten", libc::IEXTEN),
282 ("echo", libc::ECHO),
283 ("echoe", libc::ECHOE),
284 ("echok", libc::ECHOK),
285 ("echonl", libc::ECHONL),
286 ("noflsh", libc::NOFLSH),
287 ("tostop", libc::TOSTOP),
288 ("echoprt", libc::ECHOPRT),
289 ("echoctl", libc::ECHOCTL),
290 ("echoke", libc::ECHOKE),
291 ("flusho", libc::FLUSHO),
292];
293
294fn csize_str(cflag: libc::tcflag_t) -> &'static str {
296 match cflag & libc::CSIZE {
297 libc::CS5 => "cs5",
298 libc::CS6 => "cs6",
299 libc::CS7 => "cs7",
300 libc::CS8 => "cs8",
301 _ => "cs8",
302 }
303}
304
305fn print_flags(parts: &mut Vec<String>, flags: libc::tcflag_t, entries: &[(&str, libc::tcflag_t)]) {
307 for &(name, flag) in entries {
308 if flags & flag != 0 {
309 parts.push(name.to_string());
310 } else {
311 parts.push(format!("-{}", name));
312 }
313 }
314}
315
316fn print_wrapped(items: &[String], sep: &str, max_cols: usize) {
319 let mut line = String::new();
320 for item in items {
321 let add_len = if line.is_empty() {
322 item.len()
323 } else {
324 sep.len() + item.len()
325 };
326 if !line.is_empty() && line.len() + add_len > max_cols {
327 println!("{}", line);
328 line.clear();
329 }
330 if line.is_empty() {
331 line.push_str(item);
332 } else {
333 line.push_str(sep);
334 line.push_str(item);
335 }
336 }
337 if !line.is_empty() {
338 println!("{}", line);
339 }
340}
341
342pub fn print_all(termios: &libc::termios, fd: i32) {
344 let ispeed = unsafe { libc::cfgetispeed(termios) };
345 let ospeed = unsafe { libc::cfgetospeed(termios) };
346
347 let wrap_cols = if unsafe { libc::isatty(1) } == 1 {
349 get_winsize(1).map(|ws| ws.ws_col as usize).unwrap_or(80)
350 } else {
351 80
352 };
353
354 let speed_str = if ispeed == ospeed {
356 format!("speed {} baud", baud_to_num(ospeed))
357 } else {
358 format!(
359 "speed {} baud; ispeed {} baud; ospeed {} baud",
360 baud_to_num(ospeed),
361 baud_to_num(ispeed),
362 baud_to_num(ospeed)
363 )
364 };
365 let ws_str = if let Ok(ws) = get_winsize(fd) {
366 format!("; rows {}; columns {}", ws.ws_row, ws.ws_col)
367 } else {
368 String::new()
369 };
370 #[cfg(target_os = "linux")]
372 let line_str = format!("; line = {}", termios.c_line);
373 #[cfg(not(target_os = "linux"))]
374 let line_str = String::new();
375 println!("{}{}{};", speed_str, ws_str, line_str);
376
377 let mut cc_parts: Vec<String> = Vec::new();
379 for &(name, idx) in SPECIAL_CHARS_ALL.iter() {
380 let formatted = if name == "min" || name == "time" {
382 termios.c_cc[idx].to_string()
383 } else {
384 format_cc(termios.c_cc[idx])
385 };
386 cc_parts.push(format!("{} = {};", name, formatted));
387 }
388 print_wrapped(&cc_parts, " ", wrap_cols);
389
390 let mut parts: Vec<String> = Vec::new();
394 let mut control_items: Vec<String> = Vec::new();
396 for &(name, flag) in &[("parenb", libc::PARENB), ("parodd", libc::PARODD)] {
398 if termios.c_cflag & flag != 0 {
399 control_items.push(name.to_string());
400 } else {
401 control_items.push(format!("-{}", name));
402 }
403 }
404 #[cfg(target_os = "linux")]
406 {
407 if termios.c_cflag & libc::CMSPAR != 0 {
408 control_items.push("cmspar".to_string());
409 } else {
410 control_items.push("-cmspar".to_string());
411 }
412 }
413 control_items.push(csize_str(termios.c_cflag).to_string());
415 for &(name, flag) in &[
417 ("hupcl", libc::HUPCL),
418 ("cstopb", libc::CSTOPB),
419 ("cread", libc::CREAD),
420 ("clocal", libc::CLOCAL),
421 ] {
422 if termios.c_cflag & flag != 0 {
423 control_items.push(name.to_string());
424 } else {
425 control_items.push(format!("-{}", name));
426 }
427 }
428 #[cfg(target_os = "linux")]
430 {
431 if termios.c_cflag & libc::CRTSCTS != 0 {
432 control_items.push("crtscts".to_string());
433 } else {
434 control_items.push("-crtscts".to_string());
435 }
436 }
437 print_wrapped(&control_items, " ", wrap_cols);
438
439 parts.clear();
441 print_flags(&mut parts, termios.c_iflag, INPUT_FLAGS);
442 print_wrapped(&parts, " ", wrap_cols);
443
444 parts.clear();
446 print_flags(&mut parts, termios.c_oflag, OUTPUT_FLAGS);
447 #[cfg(target_os = "linux")]
449 {
450 let mut seen_masks: Vec<libc::tcflag_t> = Vec::new();
452 for &(name, val, mask) in OUTPUT_DELAY_FLAGS {
453 if !seen_masks.contains(&mask) && (termios.c_oflag & mask) == val {
454 parts.push(name.to_string());
455 seen_masks.push(mask);
456 }
457 }
458 }
459 print_wrapped(&parts, " ", wrap_cols);
460
461 parts.clear();
463 print_flags(&mut parts, termios.c_lflag, LOCAL_FLAGS);
464 print_wrapped(&parts, " ", wrap_cols);
465}
466
467pub fn parse_control_char(s: &str) -> Option<libc::cc_t> {
469 if s == "^-" || s == "undef" {
470 Some(0)
471 } else if s == "^?" {
472 Some(0x7f)
473 } else if s.len() == 2 && s.starts_with('^') {
474 let ch = s.as_bytes()[1];
475 if ch >= b'@' && ch <= b'_' {
476 Some(ch - b'@')
477 } else if ch >= b'a' && ch <= b'z' {
478 Some(ch - b'a' + 1)
479 } else {
480 None
481 }
482 } else if s.len() == 1 {
483 Some(s.as_bytes()[0])
484 } else {
485 s.parse::<u8>().ok()
487 }
488}
489
490pub fn set_sane(termios: &mut libc::termios) {
492 termios.c_iflag = libc::BRKINT | libc::ICRNL | libc::IMAXBEL | libc::IXON;
494 #[cfg(target_os = "linux")]
495 {
496 termios.c_iflag |= libc::IUTF8;
497 }
498
499 termios.c_oflag = libc::OPOST | libc::ONLCR;
501
502 #[cfg(target_os = "linux")]
504 {
505 termios.c_cflag = (termios.c_cflag & (libc::CBAUD | libc::CBAUDEX))
506 | libc::CS8
507 | libc::CREAD
508 | libc::HUPCL;
509 }
510 #[cfg(not(target_os = "linux"))]
511 {
512 let ispeed = unsafe { libc::cfgetispeed(termios) };
515 let ospeed = unsafe { libc::cfgetospeed(termios) };
516 termios.c_cflag = libc::CS8 | libc::CREAD | libc::HUPCL;
517 unsafe {
518 libc::cfsetispeed(termios, ispeed);
519 libc::cfsetospeed(termios, ospeed);
520 }
521 }
522
523 termios.c_lflag = libc::ISIG
525 | libc::ICANON
526 | libc::IEXTEN
527 | libc::ECHO
528 | libc::ECHOE
529 | libc::ECHOK
530 | libc::ECHOCTL
531 | libc::ECHOKE;
532
533 termios.c_cc[libc::VINTR] = 0x03; termios.c_cc[libc::VQUIT] = 0x1c; termios.c_cc[libc::VERASE] = 0x7f; termios.c_cc[libc::VKILL] = 0x15; termios.c_cc[libc::VEOF] = 0x04; termios.c_cc[libc::VSTART] = 0x11; termios.c_cc[libc::VSTOP] = 0x13; termios.c_cc[libc::VSUSP] = 0x1a; termios.c_cc[libc::VREPRINT] = 0x12; termios.c_cc[libc::VWERASE] = 0x17; termios.c_cc[libc::VLNEXT] = 0x16; termios.c_cc[libc::VDISCARD] = 0x0f; termios.c_cc[libc::VMIN] = 1;
547 termios.c_cc[libc::VTIME] = 0;
548}
549
550pub fn set_raw(termios: &mut libc::termios) {
552 termios.c_iflag &= !(libc::IGNBRK
554 | libc::BRKINT
555 | libc::PARMRK
556 | libc::ISTRIP
557 | libc::INLCR
558 | libc::IGNCR
559 | libc::ICRNL
560 | libc::IXON);
561 termios.c_oflag &= !libc::OPOST;
562 termios.c_lflag &= !(libc::ECHO | libc::ECHONL | libc::ICANON | libc::ISIG | libc::IEXTEN);
563 termios.c_cflag &= !(libc::CSIZE | libc::PARENB);
564 termios.c_cflag |= libc::CS8;
565 termios.c_cc[libc::VMIN] = 1;
566 termios.c_cc[libc::VTIME] = 0;
567}
568
569pub fn set_cooked(termios: &mut libc::termios) {
571 termios.c_iflag |= libc::BRKINT | libc::IGNPAR | libc::ICRNL | libc::IXON;
572 termios.c_oflag |= libc::OPOST;
573 termios.c_lflag |= libc::ISIG | libc::ICANON | libc::ECHO;
574}
575
576pub fn open_device(path: &str) -> io::Result<i32> {
578 use std::ffi::CString;
579 let cpath = CString::new(path)
580 .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid device path"))?;
581 let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY | libc::O_NONBLOCK) };
582 if fd < 0 {
583 return Err(io::Error::last_os_error());
584 }
585 Ok(fd)
586}
587
588pub fn find_special_char(name: &str) -> Option<usize> {
590 for &(n, idx) in SPECIAL_CHARS_ALL.iter() {
591 if n == name {
592 return Some(idx);
593 }
594 }
595 None
596}
597
598pub fn apply_flag(termios: &mut libc::termios, name: &str) -> bool {
600 let (negate, flag_name) = if let Some(stripped) = name.strip_prefix('-') {
601 (true, stripped)
602 } else {
603 (false, name)
604 };
605
606 for &(n, flag) in INPUT_FLAGS.iter() {
608 if n == flag_name {
609 if negate {
610 termios.c_iflag &= !flag;
611 } else {
612 termios.c_iflag |= flag;
613 }
614 return true;
615 }
616 }
617
618 for &(n, flag) in OUTPUT_FLAGS.iter() {
620 if n == flag_name {
621 if negate {
622 termios.c_oflag &= !flag;
623 } else {
624 termios.c_oflag |= flag;
625 }
626 return true;
627 }
628 }
629
630 for &(n, flag) in CONTROL_FLAGS.iter().chain(CONTROL_FLAGS_LINUX.iter()) {
632 if n == flag_name {
633 if negate {
634 termios.c_cflag &= !flag;
635 } else {
636 termios.c_cflag |= flag;
637 }
638 return true;
639 }
640 }
641
642 for &(n, flag) in LOCAL_FLAGS.iter() {
644 if n == flag_name {
645 if negate {
646 termios.c_lflag &= !flag;
647 } else {
648 termios.c_lflag |= flag;
649 }
650 return true;
651 }
652 }
653
654 match flag_name {
656 "cs5" => {
657 termios.c_cflag = (termios.c_cflag & !libc::CSIZE) | libc::CS5;
658 return true;
659 }
660 "cs6" => {
661 termios.c_cflag = (termios.c_cflag & !libc::CSIZE) | libc::CS6;
662 return true;
663 }
664 "cs7" => {
665 termios.c_cflag = (termios.c_cflag & !libc::CSIZE) | libc::CS7;
666 return true;
667 }
668 "cs8" => {
669 termios.c_cflag = (termios.c_cflag & !libc::CSIZE) | libc::CS8;
670 return true;
671 }
672 _ => {}
673 }
674
675 false
676}
677
678pub enum SttyAction {
680 PrintAll,
681 PrintSize,
682 PrintSpeed,
683 ApplySettings,
684}
685
686pub struct SttyConfig {
688 pub action: SttyAction,
689 pub device: Option<String>,
690 pub settings: Vec<String>,
691}
692
693pub fn parse_args(args: &[String]) -> Result<SttyConfig, String> {
695 let mut action = SttyAction::ApplySettings;
696 let mut device: Option<String> = None;
697 let mut settings: Vec<String> = Vec::new();
698 let mut i = 0;
699 let mut has_explicit_action = false;
700
701 while i < args.len() {
702 match args[i].as_str() {
703 "-a" | "--all" => {
704 action = SttyAction::PrintAll;
705 has_explicit_action = true;
706 }
707 "-F" | "--file" => {
708 i += 1;
709 if i >= args.len() {
710 return Err("option requires an argument -- 'F'".to_string());
711 }
712 device = Some(args[i].clone());
713 }
714 s if s.starts_with("--file=") => {
715 device = Some(s["--file=".len()..].to_string());
716 }
717 s if s.starts_with("-F") && s.len() > 2 => {
718 device = Some(s[2..].to_string());
719 }
720 "size" => {
721 action = SttyAction::PrintSize;
722 has_explicit_action = true;
723 }
724 "speed" => {
725 action = SttyAction::PrintSpeed;
726 has_explicit_action = true;
727 }
728 _ => {
729 settings.push(args[i].clone());
730 }
731 }
732 i += 1;
733 }
734
735 if !has_explicit_action && settings.is_empty() {
736 action = SttyAction::PrintAll;
737 }
738
739 Ok(SttyConfig {
740 action,
741 device,
742 settings,
743 })
744}
745
746pub fn apply_settings(termios: &mut libc::termios, settings: &[String]) -> Result<bool, String> {
749 let mut changed = false;
750 let mut i = 0;
751
752 while i < settings.len() {
753 let arg = &settings[i];
754
755 match arg.as_str() {
756 "sane" => {
757 set_sane(termios);
758 changed = true;
759 }
760 "raw" => {
761 set_raw(termios);
762 changed = true;
763 }
764 "-raw" | "cooked" => {
765 set_cooked(termios);
766 changed = true;
767 }
768 "ispeed" => {
769 i += 1;
770 if i >= settings.len() {
771 return Err("missing argument to 'ispeed'".to_string());
772 }
773 let n: u32 = settings[i]
774 .parse()
775 .map_err(|_| format!("invalid integer argument: '{}'", settings[i]))?;
776 let baud = num_to_baud(n).ok_or_else(|| format!("invalid speed: '{}'", n))?;
777 unsafe {
778 libc::cfsetispeed(termios, baud);
779 }
780 changed = true;
781 }
782 "ospeed" => {
783 i += 1;
784 if i >= settings.len() {
785 return Err("missing argument to 'ospeed'".to_string());
786 }
787 let n: u32 = settings[i]
788 .parse()
789 .map_err(|_| format!("invalid integer argument: '{}'", settings[i]))?;
790 let baud = num_to_baud(n).ok_or_else(|| format!("invalid speed: '{}'", n))?;
791 unsafe {
792 libc::cfsetospeed(termios, baud);
793 }
794 changed = true;
795 }
796 _ => {
797 if let Ok(n) = arg.parse::<u32>() {
799 if let Some(baud) = num_to_baud(n) {
800 unsafe {
801 libc::cfsetispeed(termios, baud);
802 libc::cfsetospeed(termios, baud);
803 }
804 changed = true;
805 i += 1;
806 continue;
807 }
808 }
809
810 if let Some(idx) = find_special_char(arg) {
812 i += 1;
813 if i >= settings.len() {
814 return Err(format!("missing argument to '{}'", arg));
815 }
816 let cc = parse_control_char(&settings[i])
817 .ok_or_else(|| format!("invalid integer argument: '{}'", settings[i]))?;
818 termios.c_cc[idx] = cc;
819 changed = true;
820 i += 1;
821 continue;
822 }
823
824 if !apply_flag(termios, arg) {
826 return Err(format!("invalid argument '{}'", arg));
827 }
828 changed = true;
829 }
830 }
831
832 i += 1;
833 }
834
835 Ok(changed)
836}