1use crate::Engine;
10use crate::build::PackSpec;
11use crate::eqtb::{Loc, Val};
12use crate::glue::Glue;
13use crate::input::{Level, TokKind};
14use crate::maincontrol::{IGNORE_DEPTH, Mode};
15use crate::nodes::{NodeData, NodeRef, glue_sub, id};
16use crate::primitives_gen::{assign_dimen as dp, assign_glue as gp, assign_int as ip};
17use crate::save::GroupCode;
18use crate::typeset::{
19 AWFUL_BAD, DEPLORABLE, EJECT_PENALTY, INF_BAD, INF_PENALTY, PageContents, PageIns, mark_code,
20};
21
22#[inline]
25pub fn precedes_break(t: u8) -> bool {
26 t < id::MATH
27}
28
29impl Engine {
30 pub fn prune_page_top(&mut self, p: NodeRef, save: bool) -> NodeRef {
36 let head = self.nodes.new_temp();
37 self.nodes.link(head, p);
38 let mut prev_p = head;
39 let mut p = p;
40 let mut r = NodeRef::NULL;
41 while !p.is_null() {
42 match self.nodes.id(p) {
43 id::HLIST | id::VLIST | id::RULE => {
44 let mut g = self.eqtb.glue(gp::SPLITTOPSKIP);
45 let h = self.node_height(p);
46 g.width = if g.width > h { g.width - h } else { 0 };
47 let q = self.new_glue_node(g, glue_sub::SPLIT_TOP_SKIP);
48 self.nodes.link(prev_p, q);
49 self.nodes.link(q, p);
50 break;
51 }
52 id::GLUE | id::KERN | id::PENALTY => {
53 let q = p;
54 p = self.nodes.next(q);
55 self.nodes.set_next(q, NodeRef::NULL);
56 self.nodes.set_next(prev_p, p);
57 if !p.is_null() {
58 self.nodes.set_prev(p, prev_p);
59 }
60 if save {
61 if self.ts.disc_ptr[3].is_null() {
62 self.ts.disc_ptr[3] = q;
63 } else {
64 self.nodes.link(r, q);
65 }
66 r = q;
67 } else {
68 self.nodes.flush_list(q);
69 }
70 }
71 _ => {
72 prev_p = p;
73 p = self.nodes.next(prev_p);
74 }
75 }
76 }
77 let first = self.nodes.next(head);
78 self.nodes.free(head);
79 if !first.is_null() {
80 self.nodes.set_prev(first, NodeRef::NULL);
81 }
82 first
83 }
84
85 pub fn vert_break(&mut self, p: NodeRef, h: i32, d: i32) -> (NodeRef, i32) {
89 let mut prev_p = p;
90 let mut p = p;
91 let mut least_cost = AWFUL_BAD;
92 let mut best_place = NodeRef::NULL;
93 let mut best_hpd = 0;
94 let mut cur_height = 0i32;
95 let mut stretch = [0i32; 5];
96 let mut shrink = 0i32;
97 let mut prev_dp = 0i32;
98 loop {
99 let mut pi: Option<i32> = None;
101 let mut update = false;
102 if p.is_null() {
103 pi = Some(EJECT_PENALTY);
104 } else {
105 match self.nodes.id(p) {
106 id::HLIST | id::VLIST | id::RULE => {
107 cur_height += prev_dp + self.node_height(p);
108 prev_dp = self.node_depth(p);
109 }
110 id::GLUE => {
111 if precedes_break(self.nodes.id(prev_p)) {
112 pi = Some(0);
113 } else {
114 update = true;
115 }
116 }
117 id::KERN => {
118 let n = self.nodes.next(p);
119 let t = if n.is_null() {
120 id::PENALTY
121 } else {
122 self.nodes.id(n)
123 };
124 if t == id::GLUE {
125 pi = Some(0);
126 } else {
127 update = true;
128 }
129 }
130 id::PENALTY => pi = Some(self.nodes.penalty(p)),
131 _ => {}
132 }
133 }
134 if let Some(pi) = pi {
135 if pi < INF_PENALTY {
136 let b = if cur_height < h {
137 if stretch[1..].iter().any(|&s| s != 0) {
138 0
139 } else {
140 retex_arith::badness(h - cur_height, stretch[0])
141 }
142 } else if cur_height - h > shrink {
143 AWFUL_BAD
144 } else {
145 retex_arith::badness(cur_height - h, shrink)
146 };
147 let b = if b < AWFUL_BAD {
148 if pi <= EJECT_PENALTY {
149 pi
150 } else if b < INF_BAD {
151 b + pi
152 } else {
153 DEPLORABLE
154 }
155 } else {
156 b
157 };
158 if b <= least_cost {
159 best_place = p;
160 least_cost = b;
161 best_hpd = cur_height + prev_dp;
162 }
163 if b == AWFUL_BAD || pi <= EJECT_PENALTY {
164 return (best_place, best_hpd);
165 }
166 }
167 let t = self.nodes.id(p);
168 if t == id::GLUE || t == id::KERN {
169 update = true;
170 }
171 }
172 if update {
173 if self.nodes.id(p) == id::KERN {
174 cur_height += prev_dp + self.nodes.width(p);
175 } else {
176 let mut q = self.nodes.glue_spec(p);
177 stretch[(q.stretch_order as usize).min(4)] += q.stretch;
178 shrink += q.shrink;
179 if q.shrink_order != 0 && q.shrink != 0 {
180 const MSG: &str = "Infinite glue shrinkage found in box being split";
181 if self.eqtb.int(ip::IGNOREPRIMITIVEERROR) & 1 != 0 {
182 self.printer.print_nl("ignored error ");
185 self.printer.print_str(MSG);
186 } else {
187 self.print_err(MSG);
188 self.help(&[
189 "The box you are \\vsplitting contains some infinitely",
190 "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",
191 "Such glue doesn't belong there; but you can safely proceed,",
192 "since the offensive shrinkability has been made finite.",
193 ]);
194 self.error();
195 }
196 q.shrink_order = 0;
197 if let NodeData::Glue { spec, .. } = self.nodes.data_mut(p) {
198 spec.shrink_order = 0;
199 }
200 }
201 cur_height += prev_dp + q.width;
202 }
203 prev_dp = 0;
204 }
205 if prev_dp > d {
206 cur_height = cur_height + prev_dp - d;
207 prev_dp = d;
208 }
209 prev_p = p;
210 p = self.nodes.next(prev_p);
211 }
212 }
213
214 pub fn vsplit(&mut self, n: u32, h: i32) -> NodeRef {
216 let v = self.eqtb.boxes[n as usize].val;
217 let sd = std::mem::take(&mut self.ts.disc_ptr[3]);
218 self.nodes.flush_list(sd);
219 for m in self.marks.classes.values_mut() {
220 m[mark_code::SPLIT_FIRST] = None;
221 m[mark_code::SPLIT_BOT] = None;
222 }
223 if v.is_null() {
224 return NodeRef::NULL;
225 }
226 if self.nodes.id(v) != id::VLIST {
227 self.print_err("");
228 self.print_esc_str("vsplit");
229 self.printer.print_str(" needs a ");
230 self.print_esc_str("vbox");
231 self.help(&[
232 "The box you are trying to split is an \\hbox.",
233 "i can't split such a box, so I''ll leave it alone.",
234 ]);
235 self.error();
236 return NodeRef::NULL;
237 }
238 let smd = self.eqtb.dimen(dp::SPLITMAXDEPTH);
239 let list = self.nodes.list_ptr(v);
240 let (q, _) = self.vert_break(list, h, smd);
241 let mut p = list;
243 if p == q {
244 self.nodes.set_list_ptr(v, NodeRef::NULL);
245 } else {
246 loop {
247 if let NodeData::Mark { class, mark } = self.nodes.data(p) {
248 let (c, m) = (*class, mark.clone());
249 let e = self.marks.classes.entry(c).or_default();
250 if e[mark_code::SPLIT_FIRST].is_none() {
251 e[mark_code::SPLIT_FIRST] = Some(m.clone());
252 }
253 e[mark_code::SPLIT_BOT] = Some(m);
254 }
255 let nx = self.nodes.next(p);
256 if nx == q {
257 self.nodes.set_next(p, NodeRef::NULL);
258 break;
259 }
260 p = nx;
261 }
262 }
263 let save = self.eqtb.int(ip::SAVINGVDISCARDS) > 0;
264 let q = self.prune_page_top(q, save);
265 let p = self.nodes.list_ptr(v);
266 self.nodes.set_list_ptr(v, NodeRef::NULL);
267 self.nodes.free(v);
268 self.eqtb.boxes[n as usize].val = if q.is_null() {
269 NodeRef::NULL
270 } else {
271 self.vpack(q, PackSpec::NATURAL)
272 };
273 self.vpackage(p, PackSpec::Exactly(h), smd)
274 }
275
276 fn box_error(&mut self, n: u32) {
280 self.error();
281 self.begin_diagnostic();
282 self.printer.print_nl("The following box has been deleted:");
283 let b = self.eqtb.boxes[n as usize].val;
284 self.show_box(b);
285 self.end_diagnostic(true);
286 self.nodes.flush_list(b);
287 self.eqtb.boxes[n as usize].val = NodeRef::NULL;
288 }
289
290 fn ensure_vbox(&mut self, n: u32) {
292 let p = self.eqtb.boxes[n as usize].val;
293 if !p.is_null() && self.nodes.id(p) == id::HLIST {
294 self.print_err("Insertions can only be added to a vbox");
295 self.help(&[
296 "Tut tut: You're trying to \\insert into a",
297 "\\box register that now contains an \\hbox.",
298 "Proceed, and I'll discard its present contents.",
299 ]);
300 self.box_error(n);
301 }
302 }
303
304 fn freeze_page_specs(&mut self, s: PageContents) {
306 self.page.contents = s;
307 self.page.so_far = [0; 8];
308 self.page.fi_stretch = 0;
309 self.page.so_far[0] = self.eqtb.dimen(dp::VSIZE);
310 self.page.max_depth = self.eqtb.dimen(dp::MAXDEPTH);
311 self.page.least_cost = AWFUL_BAD;
312 if self.eqtb.int(ip::TRACINGPAGES) > 0 {
313 self.begin_diagnostic();
314 self.printer.print_nl("%% goal height=");
315 self.printer.print_scaled(self.page.so_far[0]);
316 self.printer.print_str(", max depth=");
317 self.printer.print_scaled(self.page.max_depth);
318 self.end_diagnostic(false);
319 }
320 }
321
322 fn start_new_page(&mut self) {
324 self.page.contents = PageContents::Empty;
325 self.page.page_tail = self.page.page_head;
326 self.nodes.set_next(self.page.page_head, NodeRef::NULL);
327 self.page.last_glue = None;
328 self.page.last_penalty = 0;
329 self.page.last_kern = 0;
330 self.page.last_node_type = -1;
331 self.page.so_far[7] = 0;
332 self.page.max_depth = 0;
333 }
334
335 #[inline]
337 fn set_contrib_tail(&mut self, t: NodeRef) {
338 self.nest[0].tail = t;
339 }
340
341 pub fn build_page(&mut self) {
343 let ch = self.page.contrib_head;
344 if self.nodes.next(ch).is_null() || self.ts.output_active {
345 return;
346 }
347 loop {
348 let p = self.nodes.next(ch);
349 if p.is_null() {
350 break;
351 }
352 self.page.last_penalty = 0;
354 self.page.last_kern = 0;
355 let t = self.nodes.id(p);
356 self.page.last_node_type = crate::typeset::etex_node_type(t, self.nodes.subtype(p));
357 if t == id::GLUE {
358 self.page.last_glue = Some(self.nodes.glue_spec(p));
359 } else {
360 self.page.last_glue = None;
361 if t == id::PENALTY {
362 self.page.last_penalty = self.nodes.penalty(p);
363 } else if t == id::KERN {
364 self.page.last_kern = self.nodes.width(p);
365 }
366 }
367 enum Act {
369 Contribute,
370 UpdateHeights,
371 Recycle,
372 Break(i32),
373 }
374 let contents = self.page.contents;
375 let act = match t {
376 id::HLIST | id::VLIST | id::RULE => {
377 if contents < PageContents::BoxThere {
378 if contents == PageContents::Empty {
380 self.freeze_page_specs(PageContents::BoxThere);
381 } else {
382 self.page.contents = PageContents::BoxThere;
383 }
384 let mut g = self.eqtb.glue(gp::TOPSKIP);
385 let h = self.node_height(p);
386 g.width = if g.width > h { g.width - h } else { 0 };
387 let q = self.new_glue_node(g, glue_sub::TOP_SKIP);
388 self.nodes.link(q, p);
389 self.nodes.link(ch, q);
390 continue;
391 }
392 self.page.so_far[1] += self.page.so_far[7] + self.node_height(p);
393 self.page.so_far[7] = self.node_depth(p);
394 Act::Contribute
395 }
396 id::GLUE => {
397 if contents < PageContents::BoxThere {
398 Act::Recycle
399 } else if precedes_break(self.nodes.id(self.page.page_tail)) {
400 Act::Break(0)
401 } else {
402 Act::UpdateHeights
403 }
404 }
405 id::KERN => {
406 if contents < PageContents::BoxThere {
407 Act::Recycle
408 } else {
409 let n = self.nodes.next(p);
410 if n.is_null() {
411 return;
412 } else if self.nodes.id(n) == id::GLUE {
413 Act::Break(0)
414 } else {
415 Act::UpdateHeights
416 }
417 }
418 }
419 id::PENALTY => {
420 if contents < PageContents::BoxThere {
421 Act::Recycle
422 } else {
423 Act::Break(self.nodes.penalty(p))
424 }
425 }
426 id::INS => {
427 self.append_insertion(p);
428 Act::Contribute
429 }
430 _ => Act::Contribute,
431 };
432 let mut update = matches!(act, Act::UpdateHeights);
433 match act {
434 Act::Recycle => {
435 let n = self.nodes.next(p);
436 self.nodes.set_next(ch, n);
437 self.nodes.set_next(p, NodeRef::NULL);
438 if self.eqtb.int(ip::SAVINGVDISCARDS) > 0 {
439 if self.ts.disc_ptr[2].is_null() {
440 self.ts.disc_ptr[2] = p;
441 } else {
442 let t = self.ts.disc_ptr[1];
443 self.nodes.link(t, p);
444 }
445 self.ts.disc_ptr[1] = p;
446 } else {
447 self.nodes.flush_list(p);
448 }
449 continue;
450 }
451 Act::Break(pi) => {
452 if pi < INF_PENALTY {
453 let b = self.page_badness();
454 let ip_ = self.insert_penalties;
455 let mut c = if b < AWFUL_BAD {
456 if pi <= EJECT_PENALTY {
457 pi
458 } else if b < INF_BAD {
459 b + pi + ip_
460 } else {
461 DEPLORABLE
462 }
463 } else {
464 b
465 };
466 if ip_ >= 10000 {
467 c = AWFUL_BAD;
468 }
469 if self.eqtb.int(ip::TRACINGPAGES) > 0 {
470 self.begin_diagnostic();
471 self.printer.print_nl("%");
472 self.printer.print_str(" t=");
473 self.print_totals();
474 self.printer.print_str(" g=");
475 self.printer.print_scaled(self.page.so_far[0]);
476 self.printer.print_str(" b=");
477 if b == AWFUL_BAD {
478 self.printer.print_char('*' as u32);
479 } else {
480 self.printer.print_int(i64::from(b));
481 }
482 self.printer.print_str(" p=");
483 self.printer.print_int(i64::from(pi));
484 self.printer.print_str(" c=");
485 if c == AWFUL_BAD {
486 self.printer.print_char('*' as u32);
487 } else {
488 self.printer.print_int(i64::from(c));
489 }
490 if c <= self.page.least_cost {
491 self.printer.print_char('#' as u32);
492 }
493 self.end_diagnostic(false);
494 }
495 if c <= self.page.least_cost {
496 self.page.best_break = p;
497 self.page.best_size = self.page.so_far[0];
498 self.page.best_so_far = self.page.so_far;
499 self.page.best_infinite = self.page.fi_stretch != 0
500 || self.page.so_far[3..6].iter().any(|&s| s != 0);
501 self.page.least_cost = c;
502 for r in self.page.ins.iter_mut() {
503 r.best_ins_ptr = r.last_ins_ptr;
504 }
505 }
506 if c == AWFUL_BAD || pi <= EJECT_PENALTY {
507 self.fire_up(p);
508 if self.ts.output_active {
509 return;
510 }
511 continue;
514 }
515 }
516 if t == id::GLUE || t == id::KERN {
517 update = true;
518 }
519 }
520 _ => {}
521 }
522 if update {
523 if t == id::KERN {
524 self.page.so_far[1] += self.page.so_far[7] + self.nodes.width(p);
525 } else {
526 let q = self.nodes.glue_spec(p);
527 *self.page.stretch_mut(q.stretch_order) += q.stretch;
528 self.page.so_far[6] += q.shrink;
529 if q.shrink_order != 0 && q.shrink != 0 {
530 self.print_err("Infinite glue shrinkage found on current page");
531 self.help(&[
532 "The page about to be output contains some infinitely",
533 "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",
534 "Such glue doesn't belong there; but you can safely proceed,",
535 "since the offensive shrinkability has been made finite.",
536 ]);
537 self.error();
538 if let NodeData::Glue { spec, .. } = self.nodes.data_mut(p) {
539 spec.shrink_order = 0;
540 }
541 }
542 self.page.so_far[1] += self.page.so_far[7] + q.width;
543 }
544 self.page.so_far[7] = 0;
545 }
546 if self.page.so_far[7] > self.page.max_depth {
548 self.page.so_far[1] += self.page.so_far[7] - self.page.max_depth;
549 self.page.so_far[7] = self.page.max_depth;
550 }
551 let n = self.nodes.next(p);
553 let pt = self.page.page_tail;
554 self.nodes.link(pt, p);
555 self.page.page_tail = p;
556 self.nodes.set_next(ch, n);
557 if !n.is_null() {
558 self.nodes.set_prev(n, ch);
559 }
560 self.nodes.set_next(p, NodeRef::NULL);
561 }
562 self.set_contrib_tail(ch);
564 }
565
566 fn page_badness(&self) -> i32 {
568 let s = &self.page.so_far;
569 let (goal, total) = (s[0], s[1]);
570 if total < goal {
571 if self.page.fi_stretch != 0 || s[3] != 0 || s[4] != 0 || s[5] != 0 {
572 0
573 } else {
574 retex_arith::badness(goal - total, s[2])
575 }
576 } else if total - goal > s[6] {
577 AWFUL_BAD
578 } else {
579 retex_arith::badness(total - goal, s[6])
580 }
581 }
582
583 fn append_insertion(&mut self, p: NodeRef) {
585 if self.page.contents == PageContents::Empty {
586 self.freeze_page_specs(PageContents::InsertsOnly);
587 }
588 let n = u32::from(self.nodes.subtype(p));
589 let (pheight, pdepth, pcost, plist) = match self.nodes.data(p) {
590 NodeData::Ins {
591 height,
592 depth,
593 cost,
594 head,
595 ..
596 } => (*height, *depth, *cost, *head),
597 _ => return,
598 };
599 let idx = match self.page.ins.iter().position(|r| r.n >= n) {
600 Some(i) if self.page.ins[i].n == n => i,
601 pos => {
602 let i = pos.unwrap_or(self.page.ins.len());
604 self.ensure_vbox(n);
605 let b = self.eqtb.boxes[n as usize].val;
606 let height = if b.is_null() {
607 0
608 } else {
609 self.node_height(b) + self.node_depth(b)
610 };
611 self.page.ins.insert(
612 i,
613 PageIns {
614 n,
615 split_up: false,
616 height,
617 broken_ptr: NodeRef::NULL,
618 broken_ins: NodeRef::NULL,
619 last_ins_ptr: NodeRef::NULL,
620 best_ins_ptr: NodeRef::NULL,
621 },
622 );
623 let q = self.eqtb.skip[n as usize].val;
624 let c = self.eqtb.count[n as usize].val;
625 let h = if c == 1000 {
626 height
627 } else {
628 x_over_n(height, 1000).wrapping_mul(c)
629 };
630 self.page.so_far[0] = self.page.so_far[0] - h - q.width;
631 *self.page.stretch_mut(q.stretch_order) += q.stretch;
632 self.page.so_far[6] += q.shrink;
633 if q.shrink_order != 0 && q.shrink != 0 {
634 self.print_err("Infinite glue shrinkage inserted from ");
635 self.print_esc_str("skip");
636 self.printer.print_int(i64::from(n));
637 self.help(&[
638 "The correction glue for page breaking with insertions",
639 "must have finite shrinkability. But you may proceed,",
640 "since the offensive shrinkability has been made finite.",
641 ]);
642 self.error();
643 }
644 i
645 }
646 };
647 if self.page.ins[idx].split_up {
648 self.insert_penalties += pcost;
649 return;
650 }
651 self.page.ins[idx].last_ins_ptr = p;
652 let s = &self.page.so_far;
653 let delta = s[0] - s[1] - s[7] + s[6];
654 let c = self.eqtb.count[n as usize].val;
655 let h = if c == 1000 {
656 pheight
657 } else {
658 x_over_n(pheight, 1000).wrapping_mul(c)
659 };
660 let dimen_n = self.eqtb.dimen[n as usize].val;
661 if (h <= 0 || h <= delta) && pheight + self.page.ins[idx].height <= dimen_n {
662 self.page.so_far[0] -= h;
663 self.page.ins[idx].height += pheight;
664 } else {
665 let mut w = if c <= 0 {
667 retex_arith::MAX_DIMEN
668 } else {
669 let mut w = self.page.so_far[0] - self.page.so_far[1] - self.page.so_far[7];
670 if c != 1000 {
671 w = x_over_n(w, c).wrapping_mul(1000);
672 }
673 w
674 };
675 if w > dimen_n - self.page.ins[idx].height {
676 w = dimen_n - self.page.ins[idx].height;
677 }
678 let (q, mut bhpd) = self.vert_break(plist, w, pdepth);
679 self.page.ins[idx].height += bhpd;
680 if self.eqtb.int(ip::TRACINGPAGES) > 0 {
681 self.begin_diagnostic();
682 self.printer.print_nl("% split");
683 self.printer.print_int(i64::from(n));
684 self.printer.print_str(" to ");
685 self.printer.print_scaled(w);
686 self.printer.print_char(',' as u32);
687 self.printer.print_scaled(bhpd);
688 self.printer.print_str(" p=");
689 if q.is_null() {
690 self.printer.print_int(i64::from(EJECT_PENALTY));
691 } else if self.nodes.id(q) == id::PENALTY {
692 self.printer.print_int(i64::from(self.nodes.penalty(q)));
693 } else {
694 self.printer.print_char('0' as u32);
695 }
696 self.end_diagnostic(false);
697 }
698 if c != 1000 {
699 bhpd = x_over_n(bhpd, 1000).wrapping_mul(c);
700 }
701 self.page.so_far[0] -= bhpd;
702 let r = &mut self.page.ins[idx];
703 r.split_up = true;
704 r.broken_ptr = q;
705 r.broken_ins = p;
706 if q.is_null() {
707 self.insert_penalties += EJECT_PENALTY;
708 } else if self.nodes.id(q) == id::PENALTY {
709 self.insert_penalties += self.nodes.penalty(q);
710 }
711 }
712 }
713
714 fn fire_up(&mut self, c: NodeRef) {
716 let bpb = self.page.best_break;
717 if self.diagnostics.is_some() {
718 let s = self.page.best_so_far;
719 let forced = !bpb.is_null()
720 && self.nodes.id(bpb) == id::PENALTY
721 && self.nodes.penalty(bpb) <= EJECT_PENALTY;
722 self.page.last_break = Some(crate::pagenote::PageBreakInfo {
723 page: self.eqtb.count[0].val,
724 goal: s[0],
725 total: s[1],
726 stretch: s[2],
727 infinite: self.page.best_infinite,
728 forced,
729 moved: if forced { None } else { self.first_moved(bpb) },
730 });
731 }
732 if !bpb.is_null() && self.nodes.id(bpb) == id::PENALTY {
734 let pen = self.nodes.penalty(bpb);
735 self.geq_define(Loc::IntPar(ip::OUTPUTPENALTY), Val::Int(i64::from(pen)));
736 if let NodeData::Penalty { penalty } = self.nodes.data_mut(bpb) {
737 *penalty = INF_PENALTY;
738 }
739 } else {
740 self.geq_define(
741 Loc::IntPar(ip::OUTPUTPENALTY),
742 Val::Int(i64::from(INF_PENALTY)),
743 );
744 }
745 for m in self.marks.classes.values_mut() {
746 if m[mark_code::BOT].is_some() {
747 m[mark_code::TOP] = m[mark_code::BOT].clone();
748 m[mark_code::FIRST] = None;
749 }
750 }
751 if c == self.page.best_break {
752 self.page.best_break = NodeRef::NULL;
753 }
754 let best_break = self.page.best_break;
755 let obox = self.output_box_num() as u32;
757 if !self.eqtb.boxes[obox as usize].val.is_null() {
758 self.print_err("");
759 self.print_esc_str("box");
760 self.printer.print_int(i64::from(obox));
761 self.printer.print_str(" is not void");
762 self.help(&[
763 "You shouldn't use \\box255 except in \\output routines.",
764 "Proceed, and I'll discard its present contents.",
765 ]);
766 self.box_error(obox);
767 }
768 self.insert_penalties = 0;
769 let save_split_top_skip = self.eqtb.glue_par[gp::SPLITTOPSKIP as usize].val;
770 let holding = self.eqtb.int(ip::HOLDINGINSERTS) > 0;
771 if !holding {
772 for i in 0..self.page.ins.len() {
775 if !self.page.ins[i].best_ins_ptr.is_null() {
776 let n = self.page.ins[i].n;
777 self.ensure_vbox(n);
778 if self.eqtb.boxes[n as usize].val.is_null() {
779 let b = self.nodes.new_null_box(false);
780 self.eqtb.boxes[n as usize].val = b;
781 }
782 let b = self.eqtb.boxes[n as usize].val;
783 let l = self.nodes.list_ptr(b);
784 self.page.ins[i].last_ins_ptr = self.nodes.tail_of(l);
785 }
786 }
787 }
788 let hold_head = self.nodes.new_temp();
790 let mut q = hold_head;
791 let mut prev_p = self.page.page_head;
792 let mut p = self.nodes.next(prev_p);
793 while p != best_break && !p.is_null() {
794 let t = self.nodes.id(p);
795 if t == id::INS {
796 if !holding {
797 let n = u32::from(self.nodes.subtype(p));
798 let Some(ri) = self.page.ins.iter().position(|r| r.n == n) else {
799 prev_p = p;
800 p = self.nodes.next(prev_p);
801 continue;
802 };
803 let mut wait;
804 if self.page.ins[ri].best_ins_ptr.is_null() {
805 wait = true;
806 } else {
807 wait = false;
808 let bx = self.eqtb.boxes[n as usize].val;
809 let start = self.page.ins[ri].last_ins_ptr;
810 let il = self.nodes.list_ptr(p);
811 if start.is_null() {
812 self.nodes.set_list_ptr(bx, il);
813 } else {
814 self.nodes.link(start, il);
815 }
816 if self.page.ins[ri].best_ins_ptr == p {
817 let r = self.page.ins[ri];
820 if r.split_up && r.broken_ins == p && !r.broken_ptr.is_null() {
821 if start.is_null() && self.nodes.list_ptr(bx) == r.broken_ptr {
822 self.nodes.set_list_ptr(bx, NodeRef::NULL);
823 } else {
824 let mut s = if start.is_null() {
825 self.nodes.list_ptr(bx)
826 } else {
827 start
828 };
829 while !s.is_null() && self.nodes.next(s) != r.broken_ptr {
830 s = self.nodes.next(s);
831 }
832 if !s.is_null() {
833 self.nodes.set_next(s, NodeRef::NULL);
834 }
835 }
836 let sts = match self.nodes.data(p) {
837 NodeData::Ins { spec, .. } => *spec,
838 _ => Glue::ZERO,
839 };
840 self.eqtb.glue_par[gp::SPLITTOPSKIP as usize].val = sts;
841 let rest = self.prune_page_top(r.broken_ptr, false);
842 self.nodes.set_list_ptr(p, rest);
843 if !rest.is_null() {
844 let tmp = self.vpack(rest, PackSpec::NATURAL);
845 let hh = self.node_height(tmp) + self.node_depth(tmp);
846 self.nodes.set_list_ptr(tmp, NodeRef::NULL);
847 self.nodes.free(tmp);
848 if let NodeData::Ins { height, .. } = self.nodes.data_mut(p) {
849 *height = hh;
850 }
851 wait = true;
852 }
853 }
854 self.page.ins[ri].best_ins_ptr = NodeRef::NULL;
855 let l = self.nodes.list_ptr(bx);
856 self.nodes.set_list_ptr(bx, NodeRef::NULL);
857 self.nodes.free(bx);
858 let nb = self.vpack(l, PackSpec::NATURAL);
859 self.eqtb.boxes[n as usize].val = nb;
860 } else {
861 let l = self.nodes.list_ptr(bx);
862 self.page.ins[ri].last_ins_ptr = self.nodes.tail_of(l);
863 }
864 }
865 let nx = self.nodes.next(p);
868 self.nodes.set_next(prev_p, nx);
869 if !nx.is_null() {
870 self.nodes.set_prev(nx, prev_p);
871 }
872 self.nodes.set_next(p, NodeRef::NULL);
873 if wait {
874 self.nodes.link(q, p);
875 q = p;
876 self.insert_penalties += 1;
877 } else {
878 self.nodes.set_list_ptr(p, NodeRef::NULL);
879 self.nodes.free(p);
880 }
881 p = prev_p;
882 }
883 } else if let NodeData::Mark { class, mark } = self.nodes.data(p) {
884 let (cl, m) = (*class, mark.clone());
885 let e = self.marks.classes.entry(cl).or_default();
886 if e[mark_code::FIRST].is_none() {
887 e[mark_code::FIRST] = Some(m.clone());
888 }
889 e[mark_code::BOT] = Some(m);
890 }
891 prev_p = p;
892 p = self.nodes.next(prev_p);
893 }
894 self.eqtb.glue_par[gp::SPLITTOPSKIP as usize].val = save_split_top_skip;
895 let ch = self.page.contrib_head;
898 if !p.is_null() {
899 if self.nodes.next(ch).is_null() {
900 let pt = self.page.page_tail;
901 self.set_contrib_tail(pt);
902 }
903 let pt = self.page.page_tail;
904 let cn = self.nodes.next(ch);
905 self.nodes.set_next(pt, cn);
906 if !cn.is_null() {
907 self.nodes.set_prev(cn, pt);
908 }
909 self.nodes.link(ch, p);
910 self.nodes.set_next(prev_p, NodeRef::NULL);
911 }
912 let save_vbadness = self.eqtb.int_par[ip::VBADNESS as usize].val;
913 let save_vfuzz = self.eqtb.dimen_par[dp::VFUZZ as usize].val;
914 self.eqtb.int_par[ip::VBADNESS as usize].val = INF_BAD;
915 self.eqtb.dimen_par[dp::VFUZZ as usize].val = retex_arith::MAX_DIMEN;
916 let first = self.nodes.next(self.page.page_head);
917 let b = self.vpackage(
918 first,
919 PackSpec::Exactly(self.page.best_size),
920 self.page.max_depth,
921 );
922 self.eqtb.boxes[obox as usize].val = b;
923 self.eqtb.int_par[ip::VBADNESS as usize].val = save_vbadness;
924 self.eqtb.dimen_par[dp::VFUZZ as usize].val = save_vfuzz;
925 self.start_new_page();
926 if q != hold_head {
927 let h = self.nodes.next(hold_head);
928 self.nodes.link(self.page.page_head, h);
929 self.page.page_tail = q;
930 }
931 self.nodes.free(hold_head);
932 self.page.ins.clear();
933 for m in self.marks.classes.values_mut() {
934 if m[mark_code::TOP].is_some() && m[mark_code::FIRST].is_none() {
935 m[mark_code::FIRST] = m[mark_code::TOP].clone();
936 }
937 }
938 let output = self
939 .eqtb
940 .toks_param(crate::primitives_gen::assign_toks::OUTPUT)
941 .clone();
942 if !output.is_empty() {
943 if self.dead_cycles >= self.eqtb.int(ip::MAXDEADCYCLES) {
944 self.print_err("Output loop---");
945 self.printer.print_int(i64::from(self.dead_cycles));
946 self.printer.print_str(" consecutive dead cycles");
947 self.help(&[
948 "I've concluded that your \\output is awry; it never does a",
949 "\\shipout, so I'm shipping \\box255 out myself. Next time",
950 "increase \\maxdeadcycles if you want me to be more patient!",
951 ]);
952 self.error();
953 } else {
954 self.ts.output_active = true;
956 self.dead_cycles += 1;
957 self.push_nest(Mode::InternalVertical);
958 self.cur_list_mut().prev_depth = IGNORE_DEPTH;
959 let line = self.input.line();
960 self.cur_list_mut().mode_line = -line;
961 self.begin_token_list(output, TokKind::OutputText);
962 self.new_save_level(GroupCode::Output);
963 self.normal_paragraph();
964 self.scan_left_brace();
965 return;
966 }
967 }
968 let ph = self.page.page_head;
970 let first = self.nodes.next(ph);
971 if !first.is_null() {
972 if self.nodes.next(ch).is_null() {
973 let pt = self.page.page_tail;
974 self.set_contrib_tail(pt);
975 } else {
976 let pt = self.page.page_tail;
977 let cn = self.nodes.next(ch);
978 self.nodes.link(pt, cn);
979 }
980 self.nodes.link(ch, first);
981 self.nodes.set_next(ph, NodeRef::NULL);
982 self.page.page_tail = ph;
983 }
984 let pd = std::mem::take(&mut self.ts.disc_ptr[2]);
985 self.nodes.flush_list(pd);
986 let b = self.eqtb.boxes[obox as usize].val;
987 self.eqtb.boxes[obox as usize].val = NodeRef::NULL;
988 if !b.is_null() {
989 self.ship_out(b);
990 }
991 }
992
993 pub fn resume_output(&mut self) {
995 let ok = matches!(
996 self.input.stack.last(),
997 Some(Level::Tokens(t)) if t.loc >= t.list.len()
998 && (t.kind == TokKind::OutputText || t.kind == TokKind::BackedUp)
999 );
1000 if !ok {
1001 self.print_err("Unbalanced output routine");
1002 self.help(&[
1003 "Your sneaky output routine has problematic {'s and/or }'s.",
1004 "I can't handle that very well; good luck.",
1005 ]);
1006 self.error();
1007 let mut guard = 0;
1008 loop {
1009 self.get_token();
1010 guard += 1;
1011 let done = matches!(
1012 self.input.stack.last(),
1013 Some(Level::Tokens(t)) if t.loc >= t.list.len()
1014 );
1015 if done || guard > 1_000_000 {
1016 break;
1017 }
1018 }
1019 }
1020 self.end_token_list();
1021 self.end_graf();
1022 self.unsave();
1023 self.ts.output_active = false;
1024 self.insert_penalties = 0;
1025 let obox = self.output_box_num() as u32;
1027 if !self.eqtb.boxes[obox as usize].val.is_null() {
1028 self.print_err("Output routine didn't use all of ");
1029 self.print_esc_str("box");
1030 self.printer.print_int(i64::from(obox));
1031 self.help(&[
1032 "Your \\output commands should empty \\box255,",
1033 "e.g., by saying `\\shipout\\box255'.",
1034 "Proceed; I'll discard its present contents.",
1035 ]);
1036 self.box_error(obox);
1037 }
1038 let (head, tail) = (self.cur_list().head, self.cur_list().tail);
1039 if tail != head {
1040 let pt = self.page.page_tail;
1041 let first = self.nodes.next(head);
1042 self.nodes.link(pt, first);
1043 self.page.page_tail = tail;
1044 self.nodes.set_next(head, NodeRef::NULL);
1045 }
1046 let ph = self.page.page_head;
1047 let ch = self.page.contrib_head;
1048 let first = self.nodes.next(ph);
1049 if !first.is_null() {
1050 if self.nodes.next(ch).is_null() {
1051 let pt = self.page.page_tail;
1052 self.set_contrib_tail(pt);
1053 }
1054 let pt = self.page.page_tail;
1055 let cn = self.nodes.next(ch);
1056 self.nodes.set_next(pt, cn);
1057 if !cn.is_null() {
1058 self.nodes.set_prev(cn, pt);
1059 }
1060 self.nodes.link(ch, first);
1061 self.nodes.set_next(ph, NodeRef::NULL);
1062 self.page.page_tail = ph;
1063 }
1064 let pd = std::mem::take(&mut self.ts.disc_ptr[2]);
1065 self.nodes.flush_list(pd);
1066 self.pop_nest();
1067 self.build_page();
1068 }
1069}
1070
1071fn x_over_n(x: i32, n: i32) -> i32 {
1073 retex_arith::x_over_n(x, n).map_or(0, |d| d.quotient)
1074}