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 id::MARK | id::WHATSIT | id::BOUNDARY => Act::Contribute,
431 _ => self.luatex_fatal(
434 "pagebuilder",
435 &format!("invalid node of type {t} in vertical mode"),
436 ),
437 };
438 let mut update = matches!(act, Act::UpdateHeights);
439 match act {
440 Act::Recycle => {
441 let n = self.nodes.next(p);
442 self.nodes.set_next(ch, n);
443 self.nodes.set_next(p, NodeRef::NULL);
444 if self.eqtb.int(ip::SAVINGVDISCARDS) > 0 {
445 if self.ts.disc_ptr[2].is_null() {
446 self.ts.disc_ptr[2] = p;
447 } else {
448 let t = self.ts.disc_ptr[1];
449 self.nodes.link(t, p);
450 }
451 self.ts.disc_ptr[1] = p;
452 } else {
453 self.nodes.flush_list(p);
454 }
455 continue;
456 }
457 Act::Break(pi) => {
458 if pi < INF_PENALTY {
459 let b = self.page_badness();
460 let ip_ = self.insert_penalties;
461 let mut c = if b < AWFUL_BAD {
462 if pi <= EJECT_PENALTY {
463 pi
464 } else if b < INF_BAD {
465 b + pi + ip_
466 } else {
467 DEPLORABLE
468 }
469 } else {
470 b
471 };
472 if ip_ >= 10000 {
473 c = AWFUL_BAD;
474 }
475 if self.eqtb.int(ip::TRACINGPAGES) > 0 {
476 self.begin_diagnostic();
477 self.printer.print_nl("%");
478 self.printer.print_str(" t=");
479 self.print_totals();
480 self.printer.print_str(" g=");
481 self.printer.print_scaled(self.page.so_far[0]);
482 self.printer.print_str(" b=");
483 if b == AWFUL_BAD {
484 self.printer.print_char('*' as u32);
485 } else {
486 self.printer.print_int(i64::from(b));
487 }
488 self.printer.print_str(" p=");
489 self.printer.print_int(i64::from(pi));
490 self.printer.print_str(" c=");
491 if c == AWFUL_BAD {
492 self.printer.print_char('*' as u32);
493 } else {
494 self.printer.print_int(i64::from(c));
495 }
496 if c <= self.page.least_cost {
497 self.printer.print_char('#' as u32);
498 }
499 self.end_diagnostic(false);
500 }
501 if c <= self.page.least_cost {
502 self.page.best_break = p;
503 self.page.best_size = self.page.so_far[0];
504 self.page.best_so_far = self.page.so_far;
505 self.page.best_infinite = self.page.fi_stretch != 0
506 || self.page.so_far[3..6].iter().any(|&s| s != 0);
507 self.page.least_cost = c;
508 for r in self.page.ins.iter_mut() {
509 r.best_ins_ptr = r.last_ins_ptr;
510 }
511 }
512 if c == AWFUL_BAD || pi <= EJECT_PENALTY {
513 self.fire_up(p);
514 if self.ts.output_active {
515 return;
516 }
517 continue;
520 }
521 }
522 if t == id::GLUE || t == id::KERN {
523 update = true;
524 }
525 }
526 _ => {}
527 }
528 if update {
529 if t == id::KERN {
530 self.page.so_far[1] += self.page.so_far[7] + self.nodes.width(p);
531 } else {
532 let q = self.nodes.glue_spec(p);
533 *self.page.stretch_mut(q.stretch_order) += q.stretch;
534 self.page.so_far[6] += q.shrink;
535 if q.shrink_order != 0 && q.shrink != 0 {
536 self.print_err("Infinite glue shrinkage found on current page");
537 self.help(&[
538 "The page about to be output contains some infinitely",
539 "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",
540 "Such glue doesn't belong there; but you can safely proceed,",
541 "since the offensive shrinkability has been made finite.",
542 ]);
543 self.error();
544 if let NodeData::Glue { spec, .. } = self.nodes.data_mut(p) {
545 spec.shrink_order = 0;
546 }
547 }
548 self.page.so_far[1] += self.page.so_far[7] + q.width;
549 }
550 self.page.so_far[7] = 0;
551 }
552 if self.page.so_far[7] > self.page.max_depth {
554 self.page.so_far[1] += self.page.so_far[7] - self.page.max_depth;
555 self.page.so_far[7] = self.page.max_depth;
556 }
557 let n = self.nodes.next(p);
559 let pt = self.page.page_tail;
560 self.nodes.link(pt, p);
561 self.page.page_tail = p;
562 self.nodes.set_next(ch, n);
563 if !n.is_null() {
564 self.nodes.set_prev(n, ch);
565 }
566 self.nodes.set_next(p, NodeRef::NULL);
567 }
568 self.set_contrib_tail(ch);
570 }
571
572 fn page_badness(&self) -> i32 {
574 let s = &self.page.so_far;
575 let (goal, total) = (s[0], s[1]);
576 if total < goal {
577 if self.page.fi_stretch != 0 || s[3] != 0 || s[4] != 0 || s[5] != 0 {
578 0
579 } else {
580 retex_arith::badness(goal - total, s[2])
581 }
582 } else if total - goal > s[6] {
583 AWFUL_BAD
584 } else {
585 retex_arith::badness(total - goal, s[6])
586 }
587 }
588
589 fn append_insertion(&mut self, p: NodeRef) {
591 if self.page.contents == PageContents::Empty {
592 self.freeze_page_specs(PageContents::InsertsOnly);
593 }
594 let n = u32::from(self.nodes.subtype(p));
595 let (pheight, pdepth, pcost, plist) = match self.nodes.data(p) {
596 NodeData::Ins {
597 height,
598 depth,
599 cost,
600 head,
601 ..
602 } => (*height, *depth, *cost, *head),
603 _ => return,
604 };
605 let idx = match self.page.ins.iter().position(|r| r.n >= n) {
606 Some(i) if self.page.ins[i].n == n => i,
607 pos => {
608 let i = pos.unwrap_or(self.page.ins.len());
610 self.ensure_vbox(n);
611 let b = self.eqtb.boxes[n as usize].val;
612 let height = if b.is_null() {
613 0
614 } else {
615 self.node_height(b) + self.node_depth(b)
616 };
617 self.page.ins.insert(
618 i,
619 PageIns {
620 n,
621 split_up: false,
622 height,
623 broken_ptr: NodeRef::NULL,
624 broken_ins: NodeRef::NULL,
625 last_ins_ptr: NodeRef::NULL,
626 best_ins_ptr: NodeRef::NULL,
627 },
628 );
629 let q = self.eqtb.skip[n as usize].val;
630 let c = self.eqtb.count[n as usize].val;
631 let h = if c == 1000 {
632 height
633 } else {
634 x_over_n(height, 1000).wrapping_mul(c)
635 };
636 self.page.so_far[0] = self.page.so_far[0] - h - q.width;
637 *self.page.stretch_mut(q.stretch_order) += q.stretch;
638 self.page.so_far[6] += q.shrink;
639 if q.shrink_order != 0 && q.shrink != 0 {
640 self.print_err("Infinite glue shrinkage inserted from ");
641 self.print_esc_str("skip");
642 self.printer.print_int(i64::from(n));
643 self.help(&[
644 "The correction glue for page breaking with insertions",
645 "must have finite shrinkability. But you may proceed,",
646 "since the offensive shrinkability has been made finite.",
647 ]);
648 self.error();
649 }
650 i
651 }
652 };
653 if self.page.ins[idx].split_up {
654 self.insert_penalties += pcost;
655 return;
656 }
657 self.page.ins[idx].last_ins_ptr = p;
658 let s = &self.page.so_far;
659 let delta = s[0] - s[1] - s[7] + s[6];
660 let c = self.eqtb.count[n as usize].val;
661 let h = if c == 1000 {
662 pheight
663 } else {
664 x_over_n(pheight, 1000).wrapping_mul(c)
665 };
666 let dimen_n = self.eqtb.dimen[n as usize].val;
667 if (h <= 0 || h <= delta) && pheight + self.page.ins[idx].height <= dimen_n {
668 self.page.so_far[0] -= h;
669 self.page.ins[idx].height += pheight;
670 } else {
671 let mut w = if c <= 0 {
673 retex_arith::MAX_DIMEN
674 } else {
675 let mut w = self.page.so_far[0] - self.page.so_far[1] - self.page.so_far[7];
676 if c != 1000 {
677 w = x_over_n(w, c).wrapping_mul(1000);
678 }
679 w
680 };
681 if w > dimen_n - self.page.ins[idx].height {
682 w = dimen_n - self.page.ins[idx].height;
683 }
684 let (q, mut bhpd) = self.vert_break(plist, w, pdepth);
685 self.page.ins[idx].height += bhpd;
686 if self.eqtb.int(ip::TRACINGPAGES) > 0 {
687 self.begin_diagnostic();
688 self.printer.print_nl("% split");
689 self.printer.print_int(i64::from(n));
690 self.printer.print_str(" to ");
691 self.printer.print_scaled(w);
692 self.printer.print_char(',' as u32);
693 self.printer.print_scaled(bhpd);
694 self.printer.print_str(" p=");
695 if q.is_null() {
696 self.printer.print_int(i64::from(EJECT_PENALTY));
697 } else if self.nodes.id(q) == id::PENALTY {
698 self.printer.print_int(i64::from(self.nodes.penalty(q)));
699 } else {
700 self.printer.print_char('0' as u32);
701 }
702 self.end_diagnostic(false);
703 }
704 if c != 1000 {
705 bhpd = x_over_n(bhpd, 1000).wrapping_mul(c);
706 }
707 self.page.so_far[0] -= bhpd;
708 let r = &mut self.page.ins[idx];
709 r.split_up = true;
710 r.broken_ptr = q;
711 r.broken_ins = p;
712 if q.is_null() {
713 self.insert_penalties += EJECT_PENALTY;
714 } else if self.nodes.id(q) == id::PENALTY {
715 self.insert_penalties += self.nodes.penalty(q);
716 }
717 }
718 }
719
720 fn fire_up(&mut self, c: NodeRef) {
722 let bpb = self.page.best_break;
723 if self.diagnostics.is_some() {
724 let s = self.page.best_so_far;
725 let forced = !bpb.is_null()
726 && self.nodes.id(bpb) == id::PENALTY
727 && self.nodes.penalty(bpb) <= EJECT_PENALTY;
728 self.page.last_break = Some(crate::pagenote::PageBreakInfo {
729 page: self.eqtb.count[0].val,
730 goal: s[0],
731 total: s[1],
732 stretch: s[2],
733 infinite: self.page.best_infinite,
734 forced,
735 moved: if forced { None } else { self.first_moved(bpb) },
736 });
737 }
738 if !bpb.is_null() && self.nodes.id(bpb) == id::PENALTY {
740 let pen = self.nodes.penalty(bpb);
741 self.geq_define(Loc::IntPar(ip::OUTPUTPENALTY), Val::Int(i64::from(pen)));
742 if let NodeData::Penalty { penalty } = self.nodes.data_mut(bpb) {
743 *penalty = INF_PENALTY;
744 }
745 } else {
746 self.geq_define(
747 Loc::IntPar(ip::OUTPUTPENALTY),
748 Val::Int(i64::from(INF_PENALTY)),
749 );
750 }
751 for m in self.marks.classes.values_mut() {
752 if m[mark_code::BOT].is_some() {
753 m[mark_code::TOP] = m[mark_code::BOT].clone();
754 m[mark_code::FIRST] = None;
755 }
756 }
757 if c == self.page.best_break {
758 self.page.best_break = NodeRef::NULL;
759 }
760 let best_break = self.page.best_break;
761 let obox = self.output_box_num() as u32;
763 if !self.eqtb.boxes[obox as usize].val.is_null() {
764 self.print_err("");
765 self.print_esc_str("box");
766 self.printer.print_int(i64::from(obox));
767 self.printer.print_str(" is not void");
768 self.help(&[
769 "You shouldn't use \\box255 except in \\output routines.",
770 "Proceed, and I'll discard its present contents.",
771 ]);
772 self.box_error(obox);
773 }
774 self.insert_penalties = 0;
775 let save_split_top_skip = self.eqtb.glue_par[gp::SPLITTOPSKIP as usize].val;
776 let holding = self.eqtb.int(ip::HOLDINGINSERTS) > 0;
777 if !holding {
778 for i in 0..self.page.ins.len() {
781 if !self.page.ins[i].best_ins_ptr.is_null() {
782 let n = self.page.ins[i].n;
783 self.ensure_vbox(n);
784 if self.eqtb.boxes[n as usize].val.is_null() {
785 let b = self.nodes.new_null_box(false);
786 self.eqtb.boxes[n as usize].val = b;
787 }
788 let b = self.eqtb.boxes[n as usize].val;
789 let l = self.nodes.list_ptr(b);
790 self.page.ins[i].last_ins_ptr = self.nodes.tail_of(l);
791 }
792 }
793 }
794 let hold_head = self.nodes.new_temp();
796 let mut q = hold_head;
797 let mut prev_p = self.page.page_head;
798 let mut p = self.nodes.next(prev_p);
799 while p != best_break && !p.is_null() {
800 let t = self.nodes.id(p);
801 if t == id::INS {
802 if !holding {
803 let n = u32::from(self.nodes.subtype(p));
804 let Some(ri) = self.page.ins.iter().position(|r| r.n == n) else {
805 prev_p = p;
806 p = self.nodes.next(prev_p);
807 continue;
808 };
809 let mut wait;
810 if self.page.ins[ri].best_ins_ptr.is_null() {
811 wait = true;
812 } else {
813 wait = false;
814 let bx = self.eqtb.boxes[n as usize].val;
815 let start = self.page.ins[ri].last_ins_ptr;
816 let il = self.nodes.list_ptr(p);
817 if start.is_null() {
818 self.nodes.set_list_ptr(bx, il);
819 } else {
820 self.nodes.link(start, il);
821 }
822 if self.page.ins[ri].best_ins_ptr == p {
823 let r = self.page.ins[ri];
826 if r.split_up && r.broken_ins == p && !r.broken_ptr.is_null() {
827 if start.is_null() && self.nodes.list_ptr(bx) == r.broken_ptr {
828 self.nodes.set_list_ptr(bx, NodeRef::NULL);
829 } else {
830 let mut s = if start.is_null() {
831 self.nodes.list_ptr(bx)
832 } else {
833 start
834 };
835 while !s.is_null() && self.nodes.next(s) != r.broken_ptr {
836 s = self.nodes.next(s);
837 }
838 if !s.is_null() {
839 self.nodes.set_next(s, NodeRef::NULL);
840 }
841 }
842 let sts = match self.nodes.data(p) {
843 NodeData::Ins { spec, .. } => *spec,
844 _ => Glue::ZERO,
845 };
846 self.eqtb.glue_par[gp::SPLITTOPSKIP as usize].val = sts;
847 let rest = self.prune_page_top(r.broken_ptr, false);
848 self.nodes.set_list_ptr(p, rest);
849 if !rest.is_null() {
850 let tmp = self.vpack(rest, PackSpec::NATURAL);
851 let hh = self.node_height(tmp) + self.node_depth(tmp);
852 self.nodes.set_list_ptr(tmp, NodeRef::NULL);
853 self.nodes.free(tmp);
854 if let NodeData::Ins { height, .. } = self.nodes.data_mut(p) {
855 *height = hh;
856 }
857 wait = true;
858 }
859 }
860 self.page.ins[ri].best_ins_ptr = NodeRef::NULL;
861 let l = self.nodes.list_ptr(bx);
862 self.nodes.set_list_ptr(bx, NodeRef::NULL);
863 self.nodes.free(bx);
864 let nb = self.vpack(l, PackSpec::NATURAL);
865 self.eqtb.boxes[n as usize].val = nb;
866 } else {
867 let l = self.nodes.list_ptr(bx);
868 self.page.ins[ri].last_ins_ptr = self.nodes.tail_of(l);
869 }
870 }
871 let nx = self.nodes.next(p);
874 self.nodes.set_next(prev_p, nx);
875 if !nx.is_null() {
876 self.nodes.set_prev(nx, prev_p);
877 }
878 self.nodes.set_next(p, NodeRef::NULL);
879 if wait {
880 self.nodes.link(q, p);
881 q = p;
882 self.insert_penalties += 1;
883 } else {
884 self.nodes.set_list_ptr(p, NodeRef::NULL);
885 self.nodes.free(p);
886 }
887 p = prev_p;
888 }
889 } else if let NodeData::Mark { class, mark } = self.nodes.data(p) {
890 let (cl, m) = (*class, mark.clone());
891 let e = self.marks.classes.entry(cl).or_default();
892 if e[mark_code::FIRST].is_none() {
893 e[mark_code::FIRST] = Some(m.clone());
894 }
895 e[mark_code::BOT] = Some(m);
896 }
897 prev_p = p;
898 p = self.nodes.next(prev_p);
899 }
900 self.eqtb.glue_par[gp::SPLITTOPSKIP as usize].val = save_split_top_skip;
901 let ch = self.page.contrib_head;
904 if !p.is_null() {
905 if self.nodes.next(ch).is_null() {
906 let pt = self.page.page_tail;
907 self.set_contrib_tail(pt);
908 }
909 let pt = self.page.page_tail;
910 let cn = self.nodes.next(ch);
911 self.nodes.set_next(pt, cn);
912 if !cn.is_null() {
913 self.nodes.set_prev(cn, pt);
914 }
915 self.nodes.link(ch, p);
916 self.nodes.set_next(prev_p, NodeRef::NULL);
917 }
918 let save_vbadness = self.eqtb.int_par[ip::VBADNESS as usize].val;
919 let save_vfuzz = self.eqtb.dimen_par[dp::VFUZZ as usize].val;
920 self.eqtb.int_par[ip::VBADNESS as usize].val = INF_BAD;
921 self.eqtb.dimen_par[dp::VFUZZ as usize].val = retex_arith::MAX_DIMEN;
922 let first = self.nodes.next(self.page.page_head);
923 let b = self.vpackage(
924 first,
925 PackSpec::Exactly(self.page.best_size),
926 self.page.max_depth,
927 );
928 self.eqtb.boxes[obox as usize].val = b;
929 self.eqtb.int_par[ip::VBADNESS as usize].val = save_vbadness;
930 self.eqtb.dimen_par[dp::VFUZZ as usize].val = save_vfuzz;
931 self.start_new_page();
932 if q != hold_head {
933 let h = self.nodes.next(hold_head);
934 self.nodes.link(self.page.page_head, h);
935 self.page.page_tail = q;
936 }
937 self.nodes.free(hold_head);
938 self.page.ins.clear();
939 for m in self.marks.classes.values_mut() {
940 if m[mark_code::TOP].is_some() && m[mark_code::FIRST].is_none() {
941 m[mark_code::FIRST] = m[mark_code::TOP].clone();
942 }
943 }
944 let output = self
945 .eqtb
946 .toks_param(crate::primitives_gen::assign_toks::OUTPUT)
947 .clone();
948 if !output.is_empty() {
949 if self.dead_cycles >= self.eqtb.int(ip::MAXDEADCYCLES) {
950 self.print_err("Output loop---");
951 self.printer.print_int(i64::from(self.dead_cycles));
952 self.printer.print_str(" consecutive dead cycles");
953 self.help(&[
954 "I've concluded that your \\output is awry; it never does a",
955 "\\shipout, so I'm shipping \\box255 out myself. Next time",
956 "increase \\maxdeadcycles if you want me to be more patient!",
957 ]);
958 self.error();
959 } else {
960 self.ts.output_active = true;
962 self.dead_cycles += 1;
963 self.push_nest(Mode::InternalVertical);
964 self.cur_list_mut().prev_depth = IGNORE_DEPTH;
965 let line = self.input.line();
966 self.cur_list_mut().mode_line = -line;
967 self.begin_token_list(output, TokKind::OutputText);
968 self.new_save_level(GroupCode::Output);
969 self.normal_paragraph();
970 self.scan_left_brace();
971 return;
972 }
973 }
974 let ph = self.page.page_head;
976 let first = self.nodes.next(ph);
977 if !first.is_null() {
978 if self.nodes.next(ch).is_null() {
979 let pt = self.page.page_tail;
980 self.set_contrib_tail(pt);
981 } else {
982 let pt = self.page.page_tail;
983 let cn = self.nodes.next(ch);
984 self.nodes.link(pt, cn);
985 }
986 self.nodes.link(ch, first);
987 self.nodes.set_next(ph, NodeRef::NULL);
988 self.page.page_tail = ph;
989 }
990 let pd = std::mem::take(&mut self.ts.disc_ptr[2]);
991 self.nodes.flush_list(pd);
992 let b = self.eqtb.boxes[obox as usize].val;
993 self.eqtb.boxes[obox as usize].val = NodeRef::NULL;
994 if !b.is_null() {
995 self.ship_out(b);
996 }
997 }
998
999 pub fn resume_output(&mut self) {
1001 let ok = matches!(
1002 self.input.stack.last(),
1003 Some(Level::Tokens(t)) if t.loc >= t.list.len()
1004 && (t.kind == TokKind::OutputText || t.kind == TokKind::BackedUp)
1005 );
1006 if !ok {
1007 self.print_err("Unbalanced output routine");
1008 self.help(&[
1009 "Your sneaky output routine has problematic {'s and/or }'s.",
1010 "I can't handle that very well; good luck.",
1011 ]);
1012 self.error();
1013 let mut guard = 0;
1014 loop {
1015 self.get_token();
1016 guard += 1;
1017 let done = matches!(
1018 self.input.stack.last(),
1019 Some(Level::Tokens(t)) if t.loc >= t.list.len()
1020 );
1021 if done || guard > 1_000_000 {
1022 break;
1023 }
1024 }
1025 }
1026 self.end_token_list();
1027 self.end_graf();
1028 self.unsave();
1029 self.ts.output_active = false;
1030 self.insert_penalties = 0;
1031 let obox = self.output_box_num() as u32;
1033 if !self.eqtb.boxes[obox as usize].val.is_null() {
1034 self.print_err("Output routine didn't use all of ");
1035 self.print_esc_str("box");
1036 self.printer.print_int(i64::from(obox));
1037 self.help(&[
1038 "Your \\output commands should empty \\box255,",
1039 "e.g., by saying `\\shipout\\box255'.",
1040 "Proceed; I'll discard its present contents.",
1041 ]);
1042 self.box_error(obox);
1043 }
1044 let (head, tail) = (self.cur_list().head, self.cur_list().tail);
1045 if tail != head {
1046 let pt = self.page.page_tail;
1047 let first = self.nodes.next(head);
1048 self.nodes.link(pt, first);
1049 self.page.page_tail = tail;
1050 self.nodes.set_next(head, NodeRef::NULL);
1051 }
1052 let ph = self.page.page_head;
1053 let ch = self.page.contrib_head;
1054 let first = self.nodes.next(ph);
1055 if !first.is_null() {
1056 if self.nodes.next(ch).is_null() {
1057 let pt = self.page.page_tail;
1058 self.set_contrib_tail(pt);
1059 }
1060 let pt = self.page.page_tail;
1061 let cn = self.nodes.next(ch);
1062 self.nodes.set_next(pt, cn);
1063 if !cn.is_null() {
1064 self.nodes.set_prev(cn, pt);
1065 }
1066 self.nodes.link(ch, first);
1067 self.nodes.set_next(ph, NodeRef::NULL);
1068 self.page.page_tail = ph;
1069 }
1070 let pd = std::mem::take(&mut self.ts.disc_ptr[2]);
1071 self.nodes.flush_list(pd);
1072 self.pop_nest();
1073 self.build_page_from("after_output");
1074 }
1075}
1076
1077fn x_over_n(x: i32, n: i32) -> i32 {
1079 retex_arith::x_over_n(x, n).map_or(0, |d| d.quotient)
1080}