352 lines
12 KiB
Rust
352 lines
12 KiB
Rust
//! Callback and progress display, ported from the callback in main.c.
|
|
|
|
use crate::log::Log;
|
|
use cdrustanoia_core::callback::*;
|
|
use cdrustanoia_core::device::CD_FRAMEWORDS;
|
|
use std::io::{self, Write};
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
pub const CALLBACK_STRINGS: [&str; 16] = [
|
|
"wrote",
|
|
"finished",
|
|
"read",
|
|
"verify",
|
|
"jitter",
|
|
"correction",
|
|
"scratch",
|
|
"scratch repair",
|
|
"skip",
|
|
"drift",
|
|
"backoff",
|
|
"overlap",
|
|
"dropped",
|
|
"duped",
|
|
"transport error",
|
|
"cache error",
|
|
];
|
|
|
|
const GRAPH: usize = 30;
|
|
|
|
pub struct Progress {
|
|
pub callbegin: i64,
|
|
pub callend: i64,
|
|
pub callscript: bool,
|
|
pub abort_on_skip: bool,
|
|
pub skipped_flag: bool,
|
|
c_sector: i64,
|
|
v_sector: i64,
|
|
dispcache: [u8; GRAPH],
|
|
last: i32,
|
|
lasttime: i64,
|
|
heartbeat: u8,
|
|
slevel: i32,
|
|
slast: i32,
|
|
stimeout: i32,
|
|
cacheerr: i32,
|
|
overlap: i32,
|
|
printit: i32,
|
|
}
|
|
|
|
impl Default for Progress {
|
|
fn default() -> Self {
|
|
Self {
|
|
callbegin: 0,
|
|
callend: 0,
|
|
callscript: false,
|
|
abort_on_skip: false,
|
|
skipped_flag: false,
|
|
c_sector: 0,
|
|
v_sector: 0,
|
|
dispcache: [b' '; GRAPH],
|
|
last: 0,
|
|
lasttime: 0,
|
|
heartbeat: b' ',
|
|
slevel: 0,
|
|
slast: 0,
|
|
stimeout: 0,
|
|
cacheerr: 0,
|
|
overlap: 0,
|
|
printit: -1,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Progress {
|
|
pub fn reset_batch(&mut self) {
|
|
self.c_sector = 0;
|
|
self.v_sector = 0;
|
|
self.dispcache = [b' '; GRAPH];
|
|
self.last = 0;
|
|
self.lasttime = 0;
|
|
self.heartbeat = b' ';
|
|
self.slevel = 0;
|
|
self.slast = 0;
|
|
self.stimeout = 0;
|
|
self.overlap = 0;
|
|
self.skipped_flag = false;
|
|
}
|
|
|
|
fn now_tenths() -> i64 {
|
|
match SystemTime::now().duration_since(UNIX_EPOCH) {
|
|
Ok(d) => d.as_secs() as i64 * 10 + (d.subsec_micros() / 100_000) as i64,
|
|
Err(_) => 0,
|
|
}
|
|
}
|
|
|
|
pub fn callback(&mut self, inpos: i64, function: i32, log: &mut Log) {
|
|
if self.callscript {
|
|
let name = if (-2..=13).contains(&function) {
|
|
CALLBACK_STRINGS[(function + 2) as usize]
|
|
} else {
|
|
""
|
|
};
|
|
let _ = writeln!(io::stderr(), "##: {} [{}] @ {}", function, name, inpos);
|
|
} else if function == PARANOIA_CB_CACHEERR {
|
|
if self.cacheerr == 0 {
|
|
let _ = write!(
|
|
io::stderr(),
|
|
"\rWARNING: The CDROM drive appears to be seeking impossibly quickly.\n\
|
|
This could be due to timer bugs, a drive that really is improbably fast,\n\
|
|
or, most likely, a bug in cdparanoia's cache modelling.\n\n\
|
|
Please consider using the -A option to perform an analysis run, then mail\n\
|
|
the cdparanoia.log file produced by the analysis to paranoia-dev@xiph.org\n\
|
|
to assist developers in correcting the problem.\n\n"
|
|
);
|
|
}
|
|
self.cacheerr += 1;
|
|
}
|
|
|
|
if function == PARANOIA_CB_SKIP {
|
|
// Keep -X functional even when progress is redirected or quiet.
|
|
self.skipped_flag = true;
|
|
}
|
|
|
|
if log.quiet {
|
|
return;
|
|
}
|
|
|
|
let osector = inpos;
|
|
let sector = inpos / CD_FRAMEWORDS;
|
|
|
|
if self.printit == -1 {
|
|
self.printit = if unsafe { libc::isatty(2) } == 1 {
|
|
1
|
|
} else {
|
|
0
|
|
};
|
|
}
|
|
|
|
if self.printit == 1 {
|
|
let position = if self.callend != self.callbegin {
|
|
((sector - self.callbegin) as f64 / (self.callend - self.callbegin) as f64
|
|
* GRAPH as f64) as i32
|
|
} else {
|
|
0
|
|
};
|
|
let aheadposition = if self.callend != self.callbegin {
|
|
((self.c_sector - self.callbegin) as f64 / (self.callend - self.callbegin) as f64
|
|
* GRAPH as f64) as i32
|
|
} else {
|
|
0
|
|
};
|
|
|
|
if function == -2 {
|
|
self.v_sector = sector;
|
|
return;
|
|
}
|
|
if function == -1 {
|
|
self.last = 8;
|
|
self.heartbeat = b'*';
|
|
self.slevel = 0;
|
|
self.v_sector = sector;
|
|
} else if position < GRAPH as i32 && position >= 0 {
|
|
let p = position as usize;
|
|
match function {
|
|
PARANOIA_CB_VERIFY => {
|
|
if self.stimeout >= 30 {
|
|
if self.overlap as i64 > CD_FRAMEWORDS {
|
|
self.slevel = 2;
|
|
} else {
|
|
self.slevel = 1;
|
|
}
|
|
}
|
|
}
|
|
PARANOIA_CB_READ => {
|
|
if sector > self.c_sector {
|
|
self.c_sector = sector;
|
|
}
|
|
}
|
|
PARANOIA_CB_FIXUP_EDGE => {
|
|
if self.stimeout >= 5 {
|
|
if self.overlap as i64 > CD_FRAMEWORDS {
|
|
self.slevel = 2;
|
|
} else {
|
|
self.slevel = 1;
|
|
}
|
|
}
|
|
if self.dispcache[p] == b' ' {
|
|
self.dispcache[p] = b'-';
|
|
}
|
|
}
|
|
PARANOIA_CB_FIXUP_ATOM => {
|
|
if self.slevel < 3 || self.stimeout > 5 {
|
|
self.slevel = 3;
|
|
}
|
|
if self.dispcache[p] == b' ' || self.dispcache[p] == b'-' {
|
|
self.dispcache[p] = b'+';
|
|
}
|
|
}
|
|
PARANOIA_CB_READERR => {
|
|
self.slevel = 6;
|
|
if self.dispcache[p] != b'V' && self.dispcache[p] != b'C' {
|
|
self.dispcache[p] = b'e';
|
|
}
|
|
}
|
|
PARANOIA_CB_CACHEERR => {
|
|
self.slevel = 8;
|
|
self.dispcache[p] = b'C';
|
|
}
|
|
PARANOIA_CB_SKIP => {
|
|
self.slevel = 8;
|
|
if self.dispcache[p] != b'C' {
|
|
self.dispcache[p] = b'V';
|
|
}
|
|
}
|
|
PARANOIA_CB_OVERLAP => {
|
|
self.overlap = osector as i32;
|
|
}
|
|
PARANOIA_CB_SCRATCH => {
|
|
self.slevel = 7;
|
|
}
|
|
PARANOIA_CB_DRIFT => {
|
|
if self.slevel < 4 || self.stimeout > 5 {
|
|
self.slevel = 4;
|
|
}
|
|
}
|
|
PARANOIA_CB_FIXUP_DROPPED | PARANOIA_CB_FIXUP_DUPED => {
|
|
self.slevel = 5;
|
|
if matches!(self.dispcache[p], b' ' | b'-' | b'+') {
|
|
self.dispcache[p] = b'!';
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
let mut smilie = "= :-)";
|
|
match self.slevel {
|
|
0 => {
|
|
smilie = if self.skipped_flag { " 8-X" } else { " :^D" };
|
|
}
|
|
1 => smilie = " :-)",
|
|
2 => smilie = " :-|",
|
|
3 => smilie = " :-P",
|
|
4 => smilie = " :-/",
|
|
5 => smilie = " 8-|",
|
|
6 => smilie = " :-0",
|
|
7 => smilie = " :-(",
|
|
8 => {
|
|
smilie = " ;-(";
|
|
self.skipped_flag = true;
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
let test = Self::now_tenths();
|
|
if self.lasttime != test || function == -1 || self.slast != self.slevel {
|
|
if self.lasttime != test || function == -1 {
|
|
self.last += 1;
|
|
self.lasttime = test;
|
|
if self.last > 7 {
|
|
self.last = 0;
|
|
}
|
|
self.stimeout += 1;
|
|
self.heartbeat = match self.last {
|
|
0 => b' ',
|
|
1 | 7 => b'.',
|
|
2 | 6 => b'o',
|
|
3 | 5 => b'0',
|
|
4 => b'O',
|
|
_ => self.heartbeat,
|
|
};
|
|
if function == -1 {
|
|
self.heartbeat = b'*';
|
|
}
|
|
}
|
|
if self.slast != self.slevel {
|
|
self.stimeout = 0;
|
|
}
|
|
self.slast = self.slevel;
|
|
|
|
let mut buffer: Vec<u8> =
|
|
if self.abort_on_skip && self.skipped_flag && function != -1 {
|
|
format!(
|
|
"\r (== PROGRESS == [{}| {:06} {:02} ] =={} {} ==) ",
|
|
" ...aborting; please wait... ",
|
|
self.v_sector,
|
|
self.overlap as i64 / CD_FRAMEWORDS,
|
|
smilie,
|
|
self.heartbeat as char
|
|
)
|
|
.into_bytes()
|
|
} else {
|
|
let disp = String::from_utf8_lossy(&self.dispcache).to_string();
|
|
if self.v_sector == 0 {
|
|
format!(
|
|
"\r (== PROGRESS == [{}| ...... {:02} ] =={} {} ==) ",
|
|
disp,
|
|
self.overlap as i64 / CD_FRAMEWORDS,
|
|
smilie,
|
|
self.heartbeat as char
|
|
)
|
|
.into_bytes()
|
|
} else {
|
|
format!(
|
|
"\r (== PROGRESS == [{}| {:06} {:02} ] =={} {} ==) ",
|
|
disp,
|
|
self.v_sector,
|
|
self.overlap as i64 / CD_FRAMEWORDS,
|
|
smilie,
|
|
self.heartbeat as char
|
|
)
|
|
.into_bytes()
|
|
}
|
|
};
|
|
|
|
if aheadposition >= 0
|
|
&& aheadposition < GRAPH as i32
|
|
&& function != -1
|
|
&& buffer.len() > 19 + aheadposition as usize
|
|
{
|
|
buffer[19 + aheadposition as usize] = b'>';
|
|
}
|
|
let _ = io::stderr().write_all(&buffer);
|
|
|
|
if function == -1 {
|
|
let mut report_bytes = buffer[1..].to_vec();
|
|
report_bytes.extend_from_slice(b"\n\n");
|
|
log.write_summary_bytes(&report_bytes);
|
|
}
|
|
}
|
|
}
|
|
|
|
if function == -1 {
|
|
self.dispcache = [b' '; GRAPH];
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn skip_flag_survives_redirected_output() {
|
|
let mut p = Progress::default();
|
|
let mut log = Log::new();
|
|
log.quiet = true;
|
|
p.callback(0, PARANOIA_CB_SKIP, &mut log);
|
|
assert!(p.skipped_flag);
|
|
}
|
|
}
|