1use crate::Engine;
10use crate::input::{Level, LineSource, TokKind};
11use crate::print::Selector;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
15pub enum Interaction {
16 Batch = 0,
18 NonStop = 1,
20 Scroll = 2,
22 ErrorStop = 3,
24}
25
26impl Interaction {
27 pub fn from_i32(n: i32) -> Interaction {
29 match n {
30 0 => Interaction::Batch,
31 1 => Interaction::NonStop,
32 2 => Interaction::Scroll,
33 _ => Interaction::ErrorStop,
34 }
35 }
36}
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
40pub enum History {
41 Spotless,
43 WarningIssued,
45 ErrorMessageIssued,
47 FatalErrorStop,
49}
50
51#[derive(Debug, Clone, Copy)]
53pub struct JumpOut;
54
55#[derive(Debug)]
57pub struct ErrorState {
58 pub interaction: Interaction,
60 pub history: History,
62 pub error_count: i32,
64 pub help: Vec<&'static str>,
66 pub help_owned: Vec<String>,
68 pub use_err_help: bool,
70 pub deletions_allowed: bool,
72 pub set_box_allowed: bool,
74 pub no_new_control_sequence: bool,
76 pub old_setting: Option<Selector>,
78 pub file_line_error: bool,
80 pub interrupt: bool,
82}
83
84impl Default for ErrorState {
85 fn default() -> Self {
86 ErrorState {
87 interaction: Interaction::ErrorStop,
88 history: History::FatalErrorStop,
89 error_count: 0,
90 help: Vec::new(),
91 help_owned: Vec::new(),
92 use_err_help: false,
93 deletions_allowed: true,
94 set_box_allowed: true,
95 no_new_control_sequence: true,
96 old_setting: None,
97 file_line_error: false,
98 interrupt: false,
99 }
100 }
101}
102
103impl Engine {
104 pub fn print_err(&mut self, msg: &str) {
106 self.printer.begin_line();
107 if !(self.err.file_line_error && self.print_file_line()) {
108 self.printer.print_str("! ");
109 }
110 if self.diagnostics.is_some() {
111 self.printer.capture = Some(Vec::new());
112 }
113 self.printer.print_str(msg);
114 }
115
116 pub fn diag_capture_start(&mut self) {
118 if self.diagnostics.is_some() {
119 self.printer.capture = Some(Vec::new());
120 }
121 }
122
123 pub fn source_location(&self) -> Option<(String, u32, String, usize)> {
126 let elc = self
127 .eqtb
128 .int(crate::primitives_gen::assign_int::ENDLINECHAR);
129 self.input.stack.iter().rev().find_map(|l| match l {
130 crate::input::Level::Text(t) if t.is_input_file => {
131 let mut chars: &[u32] = &t.buf;
132 if let Some((&last, rest)) = chars.split_last()
133 && i64::from(last) == i64::from(elc)
134 {
135 chars = rest;
136 }
137 let text: String = chars.iter().filter_map(|&c| char::from_u32(c)).collect();
138 Some((
139 String::from_utf8_lossy(&t.name).into_owned(),
140 t.line.max(0) as u32,
141 text,
142 t.loc.min(chars.len()),
143 ))
144 }
145 _ => None,
146 })
147 }
148
149 pub fn diag_capture_warning(&mut self, line: Option<u32>) {
152 let Some(text) = self.printer.capture.take() else {
153 return;
154 };
155 let message = String::from_utf8_lossy(&text).trim().replace('\n', " ");
156 let mut d = crate::diag::Diagnostic::new(crate::diag::Severity::Warning, message);
157 if let Some((file, cur, _, _)) = self.source_location() {
158 d.file = Some(file);
159 d.line = Some(line.unwrap_or(cur));
160 }
161 if let Some(list) = self.diagnostics.as_mut() {
162 list.push(d);
163 }
164 }
165
166 fn record_error(&mut self) {
168 let Some(text) = self.printer.capture.take() else {
169 return;
170 };
171 let mut message = String::from_utf8_lossy(&text).trim().replace('\n', " ");
172 if message == "Undefined control sequence" && self.cur_cs != crate::hash::NO_CS {
173 let name = self.print_to_string(|e| e.print_cs(e.cur_cs));
174 message = format!(
175 "Undefined control sequence {}",
176 String::from_utf8_lossy(&name).trim_end()
177 );
178 }
179 let mut d = crate::diag::Diagnostic::new(crate::diag::Severity::Error, message);
180 if let Some((file, line, source, column)) = self.source_location() {
181 d.file = Some(file);
182 d.line = Some(line);
183 d.source = Some(source);
184 d.column = Some(column);
185 }
186 d.help = if self.err.help_owned.is_empty() {
187 self.err.help.iter().map(|s| (*s).to_owned()).collect()
188 } else {
189 self.err.help_owned.clone()
190 };
191 if let Some(list) = self.diagnostics.as_mut() {
192 list.push(d);
193 }
194 }
195
196 pub fn print_err_bytes(&mut self, msg: &[u8]) {
198 self.print_err("");
199 self.printer.print_bytes(msg);
200 }
201
202 fn print_file_line(&mut self) -> bool {
205 let found = self.input.stack.iter().rev().find_map(|l| match l {
206 Level::Text(t) if t.is_input_file => Some((t.name.clone(), t.line)),
207 _ => None,
208 });
209 match found {
210 Some((name, line)) => {
211 self.printer.print_bytes(&name);
212 self.printer.print_char(':' as u32);
213 self.printer.print_int(line.into());
214 self.printer.print_str(": ");
215 true
216 }
217 None => false,
218 }
219 }
220
221 pub fn help(&mut self, lines: &[&'static str]) {
223 self.err.help.clear();
224 self.err.help.extend_from_slice(lines);
225 self.err.help_owned.clear();
226 }
227
228 pub fn help_owned(&mut self, lines: Vec<String>) {
230 self.err.help.clear();
231 self.err.help_owned = lines;
232 }
233
234 pub fn error(&mut self) {
237 self.record_error();
238 if self.err.history < History::ErrorMessageIssued {
239 self.err.history = History::ErrorMessageIssued;
240 }
241 self.printer.print_char('.' as u32);
242 self.show_context();
243 if self.err.interaction == Interaction::ErrorStop {
244 self.terminal_error_stop();
247 }
248 self.err.error_count += 1;
249 if self.err.error_count == 100 {
250 self.printer
251 .print_nl("(That makes 100 errors; please try again.)");
252 self.err.history = History::FatalErrorStop;
253 self.jump_out();
254 }
255 let sel = self.printer.selector;
257 if self.err.interaction > Interaction::Batch {
258 self.printer.selector = match sel {
259 Selector::TermAndLog => Selector::LogOnly,
260 Selector::TermOnly => Selector::NoPrint,
261 s => s,
262 };
263 }
264 if self.err.use_err_help {
265 self.printer.print_ln();
266 self.give_err_help();
267 } else {
268 let lines: Vec<String> = if self.err.help_owned.is_empty() {
269 self.err.help.iter().map(|s| s.to_string()).collect()
270 } else {
271 self.err.help_owned.clone()
272 };
273 let empty = lines.is_empty();
274 for l in lines {
275 self.printer.print_nl(&l);
276 }
277 if empty {
280 self.printer.begin_line();
281 } else {
282 self.printer.print_ln();
283 }
284 }
285 if self.err.use_err_help {
286 self.printer.print_ln();
287 }
288 self.err.help.clear();
289 self.err.help_owned.clear();
290 if self.err.interaction > Interaction::Batch {
291 self.printer.selector = sel;
292 }
293 self.printer.print_ln();
294 }
295
296 pub fn term_input(&mut self) -> Vec<u32> {
299 self.printer.update_terminal();
300 let mut line = String::new();
301 match std::io::stdin().read_line(&mut line) {
302 Ok(0) | Err(_) => self.fatal_error("End of file on the terminal!"),
303 Ok(_) => {}
304 }
305 let chars = crate::input::decode_line(line.trim_end_matches(['\n', '\r']).as_bytes());
306 self.printer.term_offset = 0;
307 let old = self.printer.selector;
308 self.printer.selector = match old {
309 Selector::TermAndLog => Selector::LogOnly,
310 Selector::TermOnly => Selector::NoPrint,
311 s => s,
312 };
313 for &c in &chars {
314 self.printer.print_char(c);
315 }
316 self.printer.print_ln();
317 self.printer.selector = old;
318 chars
319 }
320
321 pub fn prompt_input(&mut self, prompt: &str) -> Vec<u32> {
323 self.printer.print_str(prompt);
324 self.term_input()
325 }
326
327 fn terminal_error_stop(&mut self) {
329 loop {
330 if self.err.interaction != Interaction::ErrorStop {
331 return;
332 }
333 self.clear_for_error_prompt();
334 let line = self.prompt_input("? ");
335 let Some(&c0) = line.first() else {
336 return;
337 };
338 let c = char::from_u32(c0).unwrap_or(' ').to_ascii_uppercase();
339 match c {
340 '0'..='9' if self.err.deletions_allowed => {
341 let mut n = c as u32 - '0' as u32;
342 if let Some(&d) = line.get(1)
343 && (0x30..=0x39).contains(&d)
344 {
345 n = n * 10 + (d - 0x30);
346 }
347 let (s1, s2, s3, s4) = (
348 self.cur_tok,
349 self.cur_cmd,
350 self.cur_chr,
351 self.input.align_state,
352 );
353 self.input.align_state = 1_000_000;
354 for _ in 0..n {
355 self.get_token();
356 }
357 self.cur_tok = s1;
358 self.cur_cmd = s2;
359 self.cur_chr = s3;
360 self.input.align_state = s4;
361 self.help(&[
362 "I have just deleted some text, as you asked.",
363 "You can now delete more, or insert, or whatever.",
364 ]);
365 self.show_context();
366 continue;
367 }
368 'E' => {
369 let file = self.input.stack.iter().rev().find_map(|l| match l {
370 Level::Text(t) if t.is_input_file => Some((t.name.clone(), t.line)),
371 _ => None,
372 });
373 if let Some((name, line)) = file {
374 self.printer.print_nl("You want to edit file ");
375 self.printer.print_bytes(&name);
376 self.printer.print_str(" at line ");
377 self.printer.print_int(line.into());
378 self.err.interaction = Interaction::Scroll;
379 self.jump_out();
380 }
381 }
382 'H' => {
383 if self.err.use_err_help {
384 self.give_err_help();
385 self.err.use_err_help = false;
386 } else {
387 let lines: Vec<String> = if !self.err.help_owned.is_empty() {
388 self.err.help_owned.clone()
389 } else if !self.err.help.is_empty() {
390 self.err.help.iter().map(|s| s.to_string()).collect()
391 } else {
392 vec![
393 "Sorry, I don't know how to help in this situation.".into(),
394 "Maybe you should try asking a human?".into(),
395 ]
396 };
397 for l in lines {
398 self.printer.print_str(&l);
399 self.printer.print_ln();
400 }
401 }
402 self.help(&[
403 "Sorry, I already gave what help I could...",
404 "Maybe you should try asking a human?",
405 "An error might have occurred before I noticed any problems.",
406 "``If all else fails, read the instructions.''",
407 ]);
408 continue;
409 }
410 'I' => {
411 let text: Vec<u32> = if line.len() > 1 {
413 let mut v = vec![' ' as u32];
414 v.extend_from_slice(&line[1..]);
415 v
416 } else {
417 self.prompt_input("insert>")
418 };
419 self.begin_file_reading(crate::input::LineSource::Terminal, Vec::new(), false);
420 if let Some(Level::Text(t)) = self.input.stack.last_mut() {
421 t.buf = text;
422 t.loc = 0;
423 t.state = crate::input::LexState::MidLine;
424 }
425 return;
426 }
427 'Q' | 'R' | 'S' => {
428 self.err.error_count = 0;
429 self.printer.print_str("OK, entering ");
430 match c {
431 'Q' => {
432 self.err.interaction = Interaction::Batch;
433 self.print_esc_str("batchmode");
434 self.printer.selector = match self.printer.selector {
435 Selector::TermAndLog => Selector::LogOnly,
436 _ => Selector::NoPrint,
437 };
438 }
439 'R' => {
440 self.err.interaction = Interaction::NonStop;
441 self.print_esc_str("nonstopmode");
442 }
443 _ => {
444 self.err.interaction = Interaction::Scroll;
445 self.print_esc_str("scrollmode");
446 }
447 }
448 self.printer.print_str("...");
449 self.printer.print_ln();
450 self.printer.update_terminal();
451 return;
452 }
453 'X' => {
454 self.err.interaction = Interaction::Scroll;
455 self.jump_out();
456 }
457 _ => {}
458 }
459 self.printer
461 .print_str("Type <return> to proceed, S to scroll future error messages,");
462 self.printer
463 .print_nl("R to run without stopping, Q to run quietly,");
464 self.printer.print_nl("I to insert something, ");
465 if self
466 .input
467 .stack
468 .iter()
469 .any(|l| matches!(l, Level::Text(t) if t.is_input_file))
470 {
471 self.printer.print_str("E to edit your file,");
472 }
473 if self.err.deletions_allowed {
474 self.printer
475 .print_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
476 }
477 self.printer.print_nl("H for help, X to quit.");
478 }
479 }
480
481 fn clear_for_error_prompt(&mut self) {
484 while self.input.stack.len() > 1 {
485 match self.input.stack.last() {
486 Some(Level::Text(t))
487 if matches!(t.source, LineSource::Terminal) && t.loc >= t.buf.len() =>
488 {
489 self.end_file_reading();
490 }
491 _ => break,
492 }
493 }
494 self.printer.print_ln();
495 }
496
497 pub fn give_err_help(&mut self) {
499 let list = self
500 .eqtb
501 .toks_param(crate::primitives_gen::assign_toks::ERRHELP)
502 .clone();
503 self.token_show_list(&list, 0, list.len(), 10_000_000);
504 }
505
506 pub fn fatal_error(&mut self, s: &str) -> ! {
508 self.normalize_selector();
509 self.print_err("Emergency stop");
510 self.help_owned(vec![s.to_owned()]);
511 self.succumb();
512 }
513
514 pub fn luatex_fatal(&mut self, category: &str, msg: &str) -> ! {
518 self.normalize_selector();
519 self.printer.print_nl("! error: (");
520 self.printer.print_str(category);
521 self.printer.print_str("): ");
522 self.printer.print_str(msg);
523 let pdf = self.eqtb.int(crate::primitives_gen::assign_int::OUTPUTMODE) > 0;
524 self.printer.print_nl(if pdf {
525 "! ==> Fatal error occurred, bad output PDF file produced!"
526 } else {
527 "! ==> Fatal error occurred, bad output DVI file produced!"
528 });
529 self.printer.print_ln();
530 self.err.history = History::FatalErrorStop;
531 for n in 0..128u32 {
532 if self.printer.write_open(n) {
533 self.printer.close_write(n);
534 }
535 }
536 if self.total_pages == 0 {
537 self.printer.print_str("No pages of output.");
538 self.printer.print_ln();
539 }
540 if self.printer.log_opened() {
541 self.printer.close_log();
542 }
543 self.printer.selector = Selector::TermOnly;
544 self.printer.flush();
545 std::panic::resume_unwind(Box::new(JumpOut));
546 }
547
548 pub fn overflow(&mut self, s: &str, n: i64) -> ! {
550 self.normalize_selector();
551 self.print_err("TeX capacity exceeded, sorry [");
552 self.printer.print_str(s);
553 self.printer.print_char('=' as u32);
554 self.printer.print_int(n);
555 self.printer.print_char(']' as u32);
556 self.help(&[
557 "If you really absolutely need more capacity,",
558 "you can ask a wizard to enlarge me.",
559 ]);
560 self.succumb();
561 }
562
563 pub fn confusion(&mut self, s: &str) -> ! {
565 self.normalize_selector();
566 if self.err.history < History::ErrorMessageIssued {
567 self.print_err("This can't happen (");
568 self.printer.print_str(s);
569 self.printer.print_char(')' as u32);
570 self.help(&["I'm broken. Please show this to someone who can fix can fix"]);
571 } else {
572 self.print_err("I can't go on meeting you like this");
573 self.help(&[
574 "One of your faux pas seems to have wounded me deeply...",
575 "in fact, I'm barely conscious. Please fix it and try again.",
576 ]);
577 }
578 self.succumb();
579 }
580
581 fn succumb(&mut self) -> ! {
582 if self.err.interaction == Interaction::ErrorStop {
583 self.err.interaction = Interaction::Scroll;
584 }
585 if self.printer.log_opened() {
586 self.error();
587 }
588 self.err.history = History::FatalErrorStop;
589 self.jump_out();
590 }
591
592 pub fn jump_out(&mut self) -> ! {
594 self.close_files_and_terminate();
595 std::panic::resume_unwind(Box::new(JumpOut));
596 }
597
598 pub fn normalize_selector(&mut self) {
600 self.printer.selector = if self.printer.log_opened() {
601 Selector::TermAndLog
602 } else {
603 Selector::TermOnly
604 };
605 if self.job_name.is_none() {
606 self.open_log_file();
607 }
608 if self.err.interaction == Interaction::Batch {
609 self.printer.selector = match self.printer.selector {
610 Selector::TermAndLog => Selector::LogOnly,
611 _ => Selector::NoPrint,
612 };
613 }
614 }
615
616 pub fn check_interrupt(&mut self) {}
618
619 pub fn back_error(&mut self) {
621 self.err.interrupt = false;
622 self.back_input();
623 self.err.interrupt = true;
624 self.error();
625 }
626
627 pub fn ins_error(&mut self) {
629 self.err.interrupt = false;
630 self.back_input_inserted();
631 self.err.interrupt = true;
632 self.error();
633 }
634
635 pub fn luatex_warning(&mut self, category: &str, msg: &str) {
639 self.printer.luatex_warning(category, msg);
640 if self.err.history == History::Spotless {
641 self.err.history = History::WarningIssued;
642 }
643 }
644
645 pub fn begin_diagnostic(&mut self) {
648 self.err.old_setting = Some(self.printer.selector);
649 let online = self
650 .eqtb
651 .int(crate::primitives_gen::assign_int::TRACINGONLINE);
652 if online <= 0 && self.printer.selector == Selector::TermAndLog {
653 self.printer.selector = Selector::LogOnly;
654 if self.err.history == History::Spotless {
655 self.err.history = History::WarningIssued;
656 }
657 }
658 }
659
660 pub fn end_diagnostic(&mut self, blank_line: bool) {
662 self.printer.print_nl("");
663 if blank_line {
664 self.printer.print_ln();
665 }
666 if let Some(s) = self.err.old_setting.take() {
667 self.printer.selector = s;
668 }
669 }
670
671 pub fn show_context(&mut self) {
673 let error_context_lines = self
674 .eqtb
675 .int(crate::primitives_gen::assign_int::ERRORCONTEXTLINES);
676 let input_ptr = self.input.stack.len().saturating_sub(1);
677 let mut base_ptr = input_ptr;
678 let mut nn: i32 = -1;
679 loop {
680 let (is_text, bottom) = match &self.input.stack.get(base_ptr) {
681 Some(Level::Text(t)) => (
682 true,
683 t.is_input_file || matches!(t.source, LineSource::File { .. }) || base_ptr == 0,
684 ),
685 Some(Level::Tokens(_)) => (false, false),
686 None => (true, true),
687 };
688 let bottom_line = is_text && bottom;
689 if base_ptr == input_ptr || bottom_line || nn < error_context_lines {
690 let show = match self.input.stack.get(base_ptr) {
693 Some(Level::Tokens(t)) => {
694 base_ptr == input_ptr
695 || !matches!(t.kind, TokKind::BackedUp | TokKind::BackedUpChar)
696 || t.loc < t.list.len()
697 }
698 _ => true,
699 };
700 if show {
701 self.printer.term_just_wrapped = false;
702 self.display_context(base_ptr);
703 nn += 1;
704 if self.printer.term_just_wrapped
708 && !(bottom_line || base_ptr == 0)
709 && matches!(
710 self.printer.selector,
711 crate::print::Selector::TermAndLog | crate::print::Selector::TermOnly
712 )
713 {
714 self.printer.term_raw(b"\n");
715 }
716 }
717 } else if nn == error_context_lines {
718 self.printer.print_nl("...");
719 nn += 1;
720 }
721 if bottom_line || base_ptr == 0 {
722 break;
723 }
724 base_ptr -= 1;
725 }
726 }
727
728 fn display_context(&mut self, base_ptr: usize) {
729 let old_setting = self.printer.selector;
730 self.printer.tally = 0;
731 let level = self.input.stack[base_ptr].clone();
732 let l: usize;
733 match &level {
734 Level::Text(t) => {
735 match &t.source {
736 LineSource::Terminal => {
737 if base_ptr == 0 {
738 self.printer.print_nl("<*>");
739 } else {
740 self.printer.print_nl("<insert> ");
741 }
742 }
743 LineSource::Read(n) => {
744 self.printer.print_nl("<read ");
745 if *n == 16 {
746 self.printer.print_char('*' as u32);
747 } else {
748 self.printer.print_int(i64::from(*n));
749 }
750 self.printer.print_char('>' as u32);
751 }
752 _ => {
753 self.printer.print_nl("l.");
754 self.printer.print_int(t.line.into());
755 }
756 }
757 self.printer.print_char(' ' as u32);
758 l = self.begin_pseudoprint();
760 let end_line_char = self
761 .eqtb
762 .int(crate::primitives_gen::assign_int::ENDLINECHAR);
763 let mut j = t.buf.len();
764 if j > 0 && t.buf[j - 1] as i32 == end_line_char {
765 j -= 1;
766 }
767 for i in 0..j {
768 if i == t.loc {
769 self.set_trick_count();
770 }
771 self.printer.print_char(t.buf[i]);
772 }
773 }
774 Level::Tokens(t) => {
775 match t.kind {
776 TokKind::Parameter => self.printer.print_nl("<argument> "),
777 TokKind::UTemplate | TokKind::VTemplate => self.printer.print_nl("<template> "),
778 TokKind::BackedUp | TokKind::BackedUpChar => {
779 if t.loc >= t.list.len() {
780 self.printer.print_nl("<recently read> ")
781 } else {
782 self.printer.print_nl("<to be read again> ")
783 }
784 }
785 TokKind::Inserted => self.printer.print_nl("<inserted text> "),
786 TokKind::Macro => {
787 self.printer.print_ln();
788 self.print_cs(t.name);
789 }
790 TokKind::OutputText => self.printer.print_nl("<output> "),
791 TokKind::EveryParText => self.printer.print_nl("<everypar> "),
792 TokKind::EveryMathText => self.printer.print_nl("<everymath> "),
793 TokKind::EveryDisplayText => self.printer.print_nl("<everydisplay> "),
794 TokKind::EveryHboxText => self.printer.print_nl("<everyhbox> "),
795 TokKind::EveryVboxText => self.printer.print_nl("<everyvbox> "),
796 TokKind::EveryJobText => self.printer.print_nl("<everyjob> "),
797 TokKind::EveryCrText => self.printer.print_nl("<everycr> "),
798 TokKind::MarkText => self.printer.print_nl("<mark> "),
799 TokKind::EveryEofText => self.printer.print_nl("<everyeof> "),
800 TokKind::WriteText => self.printer.print_nl("<write> "),
801 TokKind::LocalText => self.printer.print_nl("<local> "),
802 }
803 l = self.begin_pseudoprint();
804 self.show_token_list_ctx(&t.list, t.loc, 100_000);
807 }
808 }
809 self.printer.selector = old_setting;
810 self.print_two_lines(l);
811 }
812
813 fn begin_pseudoprint(&mut self) -> usize {
814 let l = self.printer.tally;
815 self.printer.tally = 0;
816 self.printer.selector = Selector::Pseudo;
817 self.printer.trick_count = 1_000_000;
818 self.printer.trick_buf.clear();
819 self.printer.trick_buf.resize(self.printer.error_line, b' ');
820 l
821 }
822
823 pub fn set_trick_count(&mut self) {
825 let p = &mut self.printer;
826 p.first_count = p.tally;
827 p.trick_count = p.tally + 1 + p.error_line - p.half_error_line;
828 if p.trick_count < p.error_line {
829 p.trick_count = p.error_line;
830 }
831 }
832
833 fn print_two_lines(&mut self, l: usize) {
834 if self.printer.trick_count == 1_000_000 {
835 self.set_trick_count();
836 }
837 let p = &self.printer;
838 let (tally, first_count, trick_count, half, error_line) = (
839 p.tally,
840 p.first_count,
841 p.trick_count,
842 p.half_error_line,
843 p.error_line,
844 );
845 let m = if tally < trick_count {
846 tally - first_count
847 } else {
848 trick_count - first_count
849 };
850 let (start, n) = if l + first_count <= half {
851 (0, l + first_count)
852 } else {
853 self.printer.print_plain("...");
854 (l + first_count - half + 3, half)
855 };
856 let buf = self.printer.trick_buf.clone();
857 let mut bytes = Vec::new();
858 for q in start..first_count {
859 bytes.push(buf[q % error_line]);
860 }
861 self.print_raw_bytes(&bytes);
862 self.printer.print_ln();
863 for _ in 0..n {
864 self.printer.print_char(' ' as u32);
865 }
866 let p_end = if m + n <= error_line {
867 first_count + m
868 } else {
869 first_count + (error_line - n - 3)
870 };
871 bytes.clear();
872 for q in first_count..p_end {
873 bytes.push(buf[q % error_line]);
874 }
875 self.print_raw_bytes(&bytes);
876 if m + n > error_line {
877 self.printer.print_plain("...");
878 }
879 }
880
881 fn print_raw_bytes(&mut self, bytes: &[u8]) {
884 let mut i = 0;
885 while i < bytes.len() {
886 let (c, n) = crate::utf8x::decode(bytes, i);
887 if c < 32 && c != 9 && c != 10 && c != 13 {
888 self.printer.print_char(c);
890 } else {
891 self.printer.print_char(c);
892 }
893 i += n;
894 }
895 }
896}