texpackaging.c /size: 178 Kb    last modification: 2025-02-21 11:03
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7/*tex
8
9    We're essentially done with the parts of \TEX\ that are concerned with the input (|get_next|)
10    and the output (|ship_out|). So it's time to get heavily into the remaining part, which does
11    the real work of typesetting.
12
13    After lists are constructed, \TEX\ wraps them up and puts them into boxes. Two major
14    subroutines are given the responsibility for this task: |hpack| applies to horizontal lists
15    (hlists) and |vpack| applies to vertical lists (vlists). The main duty of |hpack| and |vpack|
16    is to compute the dimensions of the resulting boxes, and to adjust the glue if one of those
17    dimensions is pre-specified. The computed sizes normally enclose all of the material inside the
18    new box; but some items may stick out if negative glue is used, if the box is overfull, or if a
19    |\vbox| includes other boxes that have been shifted left.
20
21    The subroutine call |hpack(p, w, m)| returns a pointer to an |hlist_node| for a box containing
22    the hlist that starts at |p|. Parameter |w| specifies a width; and parameter |m| is either
23    |exactly| or |additional|. Thus, |hpack(p, w, exactly)| produces a box whose width is exactly
24    |w|, while |hpack(p, w, additional)| yields a box whose width is the natural width plus |w|. It
25    is convenient to define a macro called |natural| to cover the most common case, so that we can
26    say |hpack(p, natural)| to get a box that has the natural width of list |p|.
27
28    Similarly, |vpack(p, w, m)| returns a pointer to a |vlist_node| for a box containing the vlist
29    that starts at |p|. In this case |w| represents a height instead of a width; the parameter |m|
30    is interpreted as in |hpack|.
31
32    The parameters to |hpack| and |vpack| correspond to \TEX's primitives like |\hbox to 300pt|,
33    |\hbox spread 10pt|; note that |\hbox| with no dimension following it is equivalent to |\hbox
34    spread 0pt|. The |scan_spec| subroutine scans such constructions in the user's input, including
35    the mandatory left brace that follows them, and it puts the specification onto |save_stack| so
36    that the desired box can later be obtained by executing the following code:
37
38    \starttyping
39    save_state.save_ptr := save_state.save_ptr-1;
40    hpack(p, saved_value(0), saved_level(0));
41    \stoptyping
42
43     Scan a box specification and left brace:
44 */
45
46/*tex
47    The next version is the (current) end point of successive improvements. After some keys were
48    added it became important to avoid redundant checking and pushing back mismatched keys. The
49    older (maybe more readable) variants using |scan_keyword| can be found in the archives (zip
50    and git) instead of as comments here.
51*/
52
53/*tex
54
55    When scanning, special care is necessary to ensure that the special |save_stack| codes are
56    placed just below the new group code, because scanning can change |save_stack| when |\csname|
57    appears. This coincides with the text on |dir| and |attr| keywords, as these are exaclty the
58    uses of |\hbox|, |\vbox|, and |\vtop| in the input stream (the others are |\vcenter|, |\valign|,
59    and  |\halign|).
60
61    Scan a box specification and left brace comes next. Again, the more verbose, but already
62    rather optimized intermediate variants are in the archives. Improving scanners like this happen
63    stepwise in order to maintain compatibility (although \unknown\ we now quit earlier in a
64    mismatch so we're not exact compatible when an forward looking error happens.
65
66 */
67
68/* todo: options bitset */
69
70typedef enum saved_box_entries {
71    saved_box_slot_entry        = 0,
72    saved_box_context_entry     = 0,
73    saved_box_packing_entry     = 0,
74    saved_box_amount_entry      = 1,
75    saved_box_direction_entry   = 1,
76    saved_box_dir_pointer_entry = 1,
77    saved_box_attr_list_entry   = 2,
78    saved_box_pack_entry        = 2,
79    saved_box_orientation_entry = 2,
80    saved_box_anchor_entry      = 3,
81    saved_box_geometry_entry    = 3,
82    saved_box_xoffset_entry     = 3,
83    saved_box_yoffset_entry     = 4,
84    saved_box_xmove_entry       = 4,
85    saved_box_ymove_entry       = 4,
86    saved_box_shift_entry       = 5,
87    saved_box_source_entry      = 5,
88    saved_box_target_entry      = 5,
89    saved_box_class_entry       = 6,
90    saved_box_state_entry       = 6,
91    saved_box_axis_entry        = 6, 
92    saved_box_retain_entry      = 7,  
93    saved_box_options_entry     = 7,
94    saved_box_reserved_1_entry  = 7,
95    saved_box_callback_entry    = 8, /* leaders related */
96    saved_box_leaders_entry     = 8, /* leaders related */
97    saved_box_reserved_2_entry  = 8, /* leaders related */
98    saved_box_n_of_records      = 9,
99} saved_box_entries;
100
101typedef enum saved_box_options { 
102    saved_box_reverse_option     = 0x01,
103    saved_box_container_option   = 0x02,
104    saved_box_limit_option       = 0x04,
105    saved_box_mathtext_option    = 0x08,
106    saved_box_discardable_option = 0x10,
107} saved_box_options;
108
109static inline void saved_box_initialize(void)
110{
111    saved_type(0) = saved_record_0;
112    saved_type(1) = saved_record_1;
113    saved_type(2) = saved_record_2;
114    saved_type(3) = saved_record_3;
115    saved_type(4) = saved_record_4;
116    saved_type(5) = saved_record_5;
117    saved_type(6) = saved_record_6;
118    saved_type(7) = saved_record_7;
119    saved_type(8) = saved_record_8;
120    saved_record(0) = box_save_type;
121    saved_record(1) = box_save_type;
122    saved_record(2) = box_save_type;
123    saved_record(3) = box_save_type;
124    saved_record(4) = box_save_type;
125    saved_record(5) = box_save_type;
126    saved_record(6) = box_save_type;
127    saved_record(7) = box_save_type;
128    saved_record(8) = box_save_type;
129}
130
131# define saved_box_slot        saved_value_1(saved_box_slot_entry)
132# define saved_box_context     saved_value_2(saved_box_context_entry)
133# define saved_box_packing     saved_value_3(saved_box_packing_entry)
134# define saved_box_amount      saved_value_1(saved_box_amount_entry)
135# define saved_box_direction   saved_value_2(saved_box_direction_entry)
136# define saved_box_dir_pointer saved_value_3(saved_box_dir_pointer_entry)
137# define saved_box_attr_list   saved_value_1(saved_box_attr_list_entry)
138# define saved_box_pack        saved_value_2(saved_box_pack_entry)
139# define saved_box_orientation saved_value_3(saved_box_orientation_entry)
140# define saved_box_anchor      saved_value_1(saved_box_anchor_entry)
141# define saved_box_geometry    saved_value_2(saved_box_geometry_entry)
142# define saved_box_xoffset     saved_value_3(saved_box_xoffset_entry)
143# define saved_box_yoffset     saved_value_1(saved_box_yoffset_entry)
144# define saved_box_xmove       saved_value_2(saved_box_xmove_entry)
145# define saved_box_ymove       saved_value_3(saved_box_ymove_entry)
146# define saved_box_shift       saved_value_1(saved_box_shift_entry)
147# define saved_box_source      saved_value_2(saved_box_source_entry)
148# define saved_box_target      saved_value_3(saved_box_target_entry)
149# define saved_box_class       saved_value_1(saved_box_class_entry)
150# define saved_box_state       saved_value_2(saved_box_state_entry)
151# define saved_box_axis        saved_value_3(saved_box_axis_entry)
152# define saved_box_retain      saved_value_1(saved_box_retain_entry)
153# define saved_box_options     saved_value_2(saved_box_options_entry)
154# define saved_box_reserved_1  saved_value_3(saved_box_reserved_1_entry)
155# define saved_box_callback    saved_value_1(saved_box_callback_entry)
156# define saved_box_leaders     saved_value_2(saved_box_leaders_entry)
157# define saved_box_reserved_2  saved_value_3(saved_box_reserved_2_entry)
158
159void tex_show_packaging_group(const char *package)
160{
161    tex_print_str_esc(package);
162    tex_print_str(saved_box_packing == packing_exactly ? " to " : " spread ");
163    tex_print_dimension(saved_box_amount, pt_unit);
164}
165
166int tex_show_packaging_record(void)
167{
168    tex_print_str("box, ");
169    switch (saved_type(0)) { 
170        case saved_record_0:
171            tex_print_format("slot %i, context %i, packing %i", saved_value_1(0), saved_value_2(0), saved_value_3(0));
172            break;
173        case saved_record_1:
174            tex_print_format("amount %p, direction %i, pointer %i", saved_value_1(0), saved_value_2(0), saved_value_3(0));
175            break;
176        case saved_record_2:
177            tex_print_format("attrlist %i, pack %i, orientation %i", saved_value_1(0), saved_value_2(0), saved_value_3(0));
178            break;
179        case saved_record_3:
180            tex_print_format("anchor %i, geometry %i, xoffset %p", saved_value_1(0), saved_value_2(0), saved_value_3(0));
181            break;
182        case saved_record_4:
183            tex_print_format("yoffset %p, xmove %p, ymove %p", saved_value_1(0), saved_value_2(0), saved_value_3(0));
184            break;
185        case saved_record_5:       
186            tex_print_format("shift %p, source %i, target %i", saved_value_1(0), saved_value_2(0), saved_value_3(0));
187            break;
188        case saved_record_6:
189            tex_print_format("class %i, state %i, axis %i", saved_value_1(0), saved_value_2(0), saved_value_3(0));
190            break;
191        case saved_record_7:
192            tex_print_format("retain %i, options %i", saved_value_1(0), saved_value_2(0));
193            break;
194        case saved_record_8:
195            tex_print_format("callback %i, leaders %i", saved_value_1(0), saved_value_2(0));
196            break;
197        default: 
198            return 0;
199    }
200    return 1;
201}
202
203int tex_get_packaging_context(void) { return saved_box_context; }
204int tex_get_packaging_shift  (void) { return saved_box_shift; }
205
206static void tex_aux_scan_full_spec(halfword context, quarterword c, quarterword spec_direction, int just_pack, scaled shift, halfword slot, halfword callback, halfword leaders)
207{
208    quarterword spec_packing = packing_additional;
209    int spec_amount = 0;
210    halfword attrlist = null;
211    halfword orientation = 0;
212    scaled xoffset = 0;
213    scaled yoffset = 0;
214    scaled xmove = 0;
215    scaled ymove = 0;
216    halfword source = 0;
217    halfword target = 0;
218    halfword anchor = 0;
219    halfword geometry = 0;
220    halfword axis = 0;
221    halfword state = 0;
222    halfword retain = 0;
223    halfword mainclass = unset_noad_class;
224    halfword options = 0;
225    int brace = 0;
226    while (1) {
227        /*tex Maybe |migrate <int>| makes sense here. */
228        switch (tex_scan_character("tascdoxyrlmTASCDOXYRLM", 1, 1, 1)) {
229            case 0:
230                goto DONE;
231            case 't': case 'T':
232                switch (tex_scan_character("aoAO", 0, 0, 0)) {
233                    case 'a': case 'A':
234                        if (tex_scan_mandate_keyword("target", 2)) {
235                            target = tex_scan_integer(1, NULL, NULL);
236                        }
237                        break;
238                    case 'o': case 'O':
239                        spec_packing = packing_exactly;
240                        spec_amount = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
241                        break;
242                    default:
243                        tex_aux_show_keyword_error("target|to");
244                        goto DONE;
245                }
246                break;
247            case 'a': case 'A':
248                switch (tex_scan_character("dntxDNTX", 0, 0, 0)) {
249                    case 'd': case 'D':
250                        if (tex_scan_mandate_keyword("adapt", 2)) {
251                            spec_packing = packing_adapted;
252                            spec_amount = tex_scan_limited_scale(0);
253                        }
254                        break;
255                    case 't': case 'T':
256                        if (tex_scan_mandate_keyword("attr", 2)) {
257                            attrlist = tex_scan_attribute(attrlist);
258                        }
259                        break;
260                    case 'n': case 'N':
261                        if (tex_scan_mandate_keyword("anchor", 2)) {
262                            switch (tex_scan_character("sS", 0, 0, 0)) {
263                                case 's': case 'S':
264                                    anchor = tex_scan_anchors(0);
265                                    break;
266                                default:
267                                    anchor = tex_scan_anchor(0);
268                                    break;
269                            }
270                        }
271                        break;
272                    case 'x': case 'X':
273                        if (tex_scan_mandate_keyword("axis", 2)) {
274                            axis = tex_scan_box_axis();
275                        }
276                        break;
277                    default:
278                        tex_aux_show_keyword_error("adapt|attr|anchor|axis");
279                        goto DONE;
280                }
281                break;
282            case 's': case 'S':
283                switch (tex_scan_character("hpoHPO", 0, 0, 0)) {
284                    case 'h': case 'H':
285                        /*tex
286                            This is a bonus because we decoupled the shift amount from the context,
287                            where it can be somewhat confusing as that is a hybrid amount, kind, or
288                            flag field. The keyword overloads an already given |move_cmd|.
289                        */
290                        if (tex_scan_mandate_keyword("shift", 2)) {
291                            shift = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
292                        }
293                        break;
294                    case 'p': case 'P':
295                        if (tex_scan_mandate_keyword("spread", 2)) {
296                            spec_packing = packing_additional;
297                            spec_amount = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
298                        }
299                        break;
300                    case 'o': case 'O':
301                        if (tex_scan_mandate_keyword("source", 2)) {
302                            source = tex_scan_integer(1, NULL, NULL);
303                        }
304                        break;
305                    default:
306                        tex_aux_show_keyword_error("shift|spread|source");
307                        goto DONE;
308                }
309                break;
310            case 'd': case 'D':
311                switch (tex_scan_character("eiEI", 0, 0, 0)) {
312                    case 'i': case 'I':
313                        /*tex 
314                            For the sake of third party code that assumes \LUATEX's old directives 
315                            we can support |dir TRT| and |dir TLT|; only these. But \unknown\ that 
316                            would also mean we have to deal with |\textdir| and such. 
317                        */
318# if (0) 
319                        if (tex_scan_mandate_keyword("direction", 2)) {
320                            spec_direction = tex_scan_direction(0);
321                        }
322# else 
323                        if (tex_scan_keyword("rection")) {
324                            spec_direction = tex_scan_direction(0);
325} else if (tex_scan_keyword("scardable")) {
326    options |= saved_box_discardable_option;
327                        } else if (tex_scan_character("rR", 0, 0, 0)) {
328                            /*tex This is undocumented. */
329                            if (tex_scan_character("tT", 0, 1, 0)) {
330                                switch (tex_scan_character("lLrR", 0, 0, 0)) {
331                                    case 'l': case 'L': 
332                                        spec_direction = 0;
333                                        break;
334                                    case 'r': case 'R': 
335                                        spec_direction = 1;
336                                        break;
337                                    default: 
338                                        goto BADDIR;
339                                }
340                                if (! tex_scan_character("tT", 0, 0, 0)) {
341                                    goto BADDIR;
342                                }
343                            } else { 
344                              BADDIR:
345                                tex_aux_show_keyword_error("tlt|trt");
346                                goto DONE;
347                            }
348                        } else {
349//                            tex_aux_show_keyword_error("direction|dir");
350tex_aux_show_keyword_error("direction|dir|discardable");
351                            goto DONE;
352                        }
353# endif 
354                        break;
355                    case 'e': case 'E':
356                        if (tex_scan_mandate_keyword("delay", 2)) {
357                            state |= package_u_leader_delayed;
358                        }
359                        break;
360                    default:
361                        tex_aux_show_keyword_error("direction|delay");
362                        goto DONE;
363                }
364                break;
365            case 'o': case 'O':
366                if (tex_scan_mandate_keyword("orientation", 1)) {
367                    orientation = tex_scan_orientation(0);
368                }
369                break;
370            case 'x': case 'X':
371                switch (tex_scan_character("omOM", 0, 0, 0)) {
372                    case 'o': case 'O' :
373                        if (tex_scan_mandate_keyword("xoffset", 2)) {
374                            xoffset = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
375                        }
376                        break;
377                    case 'm': case 'M' :
378                        if (tex_scan_mandate_keyword("xmove", 2)) {
379                            xmove = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
380                        }
381                        break;
382                    default:
383                        tex_aux_show_keyword_error("xoffset|xmove");
384                        goto DONE;
385                }
386                break;
387            case 'y': case 'Y':
388                switch (tex_scan_character("omOM", 0, 0, 0)) {
389                    case 'o': case 'O' :
390                        if (tex_scan_mandate_keyword("yoffset", 2)) {
391                             yoffset = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
392                        }
393                        break;
394                    case 'm': case 'M' :
395                        if (tex_scan_mandate_keyword("ymove", 2)) {
396                             ymove = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
397                        }
398                        break;
399                    default:
400                        tex_aux_show_keyword_error("yoffset|ymove");
401                        goto DONE;
402                }
403                break;
404            case 'r': case 'R':
405                if (tex_scan_character("eE", 0, 0, 0)) {
406                    switch (tex_scan_character("vVtT", 0, 0, 0)) {
407                        case 'v': case 'V' :
408                            if (tex_scan_mandate_keyword("reverse", 3)) {
409                                options |= saved_box_reverse_option;
410                            }
411                            break;
412                        case 't': case 'T' :
413                            if (tex_scan_mandate_keyword("retain", 3)) {
414                                retain = tex_scan_integer(0, NULL, NULL);
415                            }
416                            break;
417                        default:
418                            tex_aux_show_keyword_error("reverse|retain");
419                            goto DONE;
420                    }
421                }
422                break;
423            case 'c': case 'C':
424                switch (tex_scan_character("olOL", 0, 0, 0)) {
425                    case 'o': case 'O' :
426                        if (tex_scan_mandate_keyword("container", 2)) {
427                            options |= saved_box_container_option;
428                        }
429                        break;
430                    case 'l': case 'L' :
431                        if (tex_scan_mandate_keyword("class", 2)) {
432                            mainclass = tex_scan_math_class_number(0);
433                        }
434                        break;
435                    default:
436                        tex_aux_show_keyword_error("container|class");
437                        goto DONE;
438                }
439                break;
440            case 'l': case 'L':
441                if (tex_scan_mandate_keyword("limit", 1)) {
442                    options |= saved_box_limit_option;
443                }
444                break;
445            case 'm': case 'M':
446                if (tex_scan_mandate_keyword("mathtext", 1)) {
447                    options |= saved_box_mathtext_option;
448                }
449                break;
450            case '{':
451                brace = 1;
452                goto DONE;
453            default:
454                goto DONE;
455        }
456    }
457  DONE:
458    if (anchor || source || target) {
459        geometry |= anchor_geometry;
460    }
461    if (orientation || xmove || ymove) {
462        geometry |= orientation_geometry;
463    }
464    if (xoffset || yoffset) {
465        geometry |= offset_geometry;
466    }
467    /*tex
468        We either build one triggered by the |attr| key or we never set it in which case we use the
469        default. As we will use it anyway, we also bump the reference, which also makes sure that
470        it will stay.
471    */
472    if (! attrlist) {
473        /* this alse sets the reference when not yet set */
474        attrlist = tex_current_attribute_list();
475    }
476    /*tex Now we're referenced. We need to preserve this over the group. */
477    add_attribute_reference(attrlist);
478    /* */
479    saved_box_initialize();
480    saved_box_slot = slot;
481    saved_box_context = context; 
482    /*tex Traditionally these two are packed into one record: */
483    saved_box_packing = spec_packing;
484    saved_box_amount = spec_amount;
485    /*tex Adjust |text_dir_ptr| for |scan_spec|: */
486    saved_box_direction = spec_direction;
487    if (spec_direction != direction_unknown) {
488        saved_box_dir_pointer = lmt_dir_state.text_dir_ptr;
489        lmt_dir_state.text_dir_ptr = tex_new_dir(normal_dir_subtype, spec_direction);
490    } else {
491        saved_box_dir_pointer = null;
492    }
493    saved_box_attr_list = attrlist;
494    saved_box_pack = just_pack;
495    saved_box_orientation = orientation;
496    saved_box_anchor = anchor;
497    saved_box_geometry = geometry;
498    saved_box_xoffset = xoffset;
499    saved_box_yoffset = yoffset;
500    saved_box_xmove = xmove;
501    saved_box_ymove = ymove;
502    saved_box_source = source;
503    saved_box_target = target;
504    saved_box_state = state;
505    saved_box_options = options;
506    saved_box_shift = shift;
507    saved_box_axis = axis;
508    saved_box_class = mainclass;
509    saved_box_retain = retain;
510    saved_box_callback = callback;
511    saved_box_leaders = leaders;
512    lmt_save_state.save_stack_data.ptr += saved_box_n_of_records;
513    tex_new_save_level(c);
514    if (! brace) {
515        tex_scan_left_brace();
516    }
517    update_tex_par_direction(spec_direction);
518    update_tex_text_direction(spec_direction);
519}
520
521/*tex
522
523    To figure out the glue setting, |hpack| and |vpack| determine how much stretchability and
524    shrinkability are present, considering all four orders of infinity. The highest order of
525    infinity that has a nonzero coefficient is then used as if no other orders were present.
526
527    For example, suppose that the given list contains six glue nodes with the respective
528    stretchabilities |3pt|, |8fill|, |5fil|, |6pt|, |-3fil|, |-8fill|. Then the total is essentially
529    |2fil|; and if a total additional space of 6pt is to be achieved by stretching, the actual
530    amounts of stretch will be |0pt|, |0pt|, |15pt|, |0pt|, |-9pt|, and |0pt|, since only |fi| glue
531    will be considered. (The |fill| glue is therefore not really stretching infinitely with respect
532    to |fil|; nobody would actually want that to happen.)
533
534    The arrays |total_stretch| and |total_shrink| are used to determine how much glue of each kind
535    is present. A global variable |last_badness| is used to implement |\badness|.
536
537*/
538
539packaging_state_info lmt_packaging_state = {
540    .total_stretch          = { 0 },
541    .total_shrink           = { 0 },  /*tex glue found by |hpack| or |vpack| */
542    .last_badness           = 0,      /*tex badness of the most recently packaged box */
543    .last_overshoot         = 0,      /*tex overshoot of the most recently packaged box */
544    .post_adjust_tail       = null,   /*tex tail of adjustment list */
545    .pre_adjust_tail        = null,
546    .post_migrate_tail      = null,   /*tex tail of migration list */
547    .pre_migrate_tail       = null,
548    .last_leftmost_char     = null,
549    .last_rightmost_char    = null,
550    .pack_begin_line        = 0,
551 /* .active_height          = { 0 }, */
552    .best_height_plus_depth = 0,
553    .previous_char_ptr      = null,
554    .font_expansion_ratio   = 0,
555    .except                 = 0,
556    .page_discards_tail     = null,
557    .page_discards_head     = null,
558    .split_discards_head    = null,
559    .split_last_height      = 0,
560    .split_last_depth       = 0,
561    .split_last_stretch     = 0,
562    .split_last_shrink      = 0,
563};
564
565/*tex
566
567    This state collects the glue found by |hpack| or |vpack|: |total_stretch| and |total_shrink|
568    and the badness of the most recently packaged box |last_badness|.
569
570    If the variable |adjust_tail| is non-null, the |hpack| routine also removes all occurrences of
571    |insert_node|, |mark_node|, and |adjust_node| items and appends the resulting material onto the
572    list that ends at location |adjust_tail|.
573
574    Tail of adjustment list is stored in |adjust_tail|. Materials in |\vadjust| used with |pre|
575    keyword will be appended to |pre_adjust_tail| instead of |adjust_tail|.
576
577    The optimizers use |last_leftmost_char| and last_rightmost_char|.
578
579    In order to provide a decent indication of where an overfull or underfull box originated, we
580    use a global variable |pack_begin_line| that is set nonzero only when |hpack| is being called
581    by the paragraph builder or the alignment finishing routine.
582
583    The source file line where the current paragraph or alignment began; a negative value denotes
584    alignment |pack_begin_line|.
585
586    Pointers to the prev and next char of an implicit kern are kept in |next_char_p| and
587    prev_char_p|.
588
589    The kern stretch and shrink code was (or had become) rather weird ... the width field is set,
590    and then used in a second calculation, repeatedly, so why is that \unknown\ maybe some some
591    weird left-over \unknown\ anyway, the values are so small that in practice they are not
592    significant at all when the backend sees them because a few hundred sp positive or negative are
593    just noise there (so adjustlevel 3 has hardly any consequence for the result but is more
594    efficient).
595
596    In the end I simplified the code because in practice these kerns can between glyphs burried in
597    discretionary nodes. Also, we don't enable it by default so let's just stick to the leftmost
598    character as reference. We can assume the same font anyway.
599
600*/
601
602static inline int tex_has_glyph_expansion(halfword a) 
603{ 
604    return 
605        ! ((glyph_options(a) & glyph_option_no_expansion) == glyph_option_no_expansion) 
606        && has_font_text_control(glyph_font(a), text_control_expansion);
607}
608
609scaled tex_char_stretch(halfword p) /* todo: move this to texfont.c and make it more efficient */
610{
611    if (tex_has_glyph_expansion(p)) {
612        halfword m = lmt_font_state.adjust_stretch;
613        if (m > 0) {
614            halfword f = glyph_font(p);
615            halfword c = glyph_character(p);
616            scaled e = tex_char_ef_from_font(f, c);
617            if (e > 0) {
618                scaled dw = tex_calculated_glyph_width(p, m) - tex_char_width_from_glyph(p);
619                if (dw > 0) {
620                    return tex_round_xn_over_d(dw, e, scaling_factor);
621                }
622            }
623        }
624    }
625    return 0;
626}
627
628scaled tex_char_shrink(halfword p) /* todo: move this to texfont.c and make it more efficient */
629{
630    if (tex_has_glyph_expansion(p)) {
631        halfword m = lmt_font_state.adjust_shrink;
632        if (m > 0) {
633            halfword f = glyph_font(p);
634            halfword c = glyph_character(p);
635            scaled e = tex_char_cf_from_font(f, c);
636            if (e > 0) {
637                scaled dw = tex_char_width_from_glyph(p) - tex_calculated_glyph_width(p, -m);
638                if (dw > 0) {
639                    return tex_round_xn_over_d(dw, e, scaling_factor);
640                }
641            }
642        }
643    }
644    return 0;
645}
646
647scaled tex_kern_stretch(halfword p)
648{
649    scaled w = kern_amount(p);
650    if (w)  {
651        halfword l = lmt_packaging_state.previous_char_ptr;
652        if (l && node_type(l) == glyph_node && tex_has_glyph_expansion(l)) {
653            scaled m = lmt_font_state.adjust_stretch;
654            if (m > 0) {
655                scaled e = tex_char_ef_from_font(glyph_font(l), glyph_character(l));
656                if (e > 0) {
657                    scaled dw = w - tex_round_xn_over_d(w, scaling_factor + m, scaling_factor);
658                    if (dw > 0) {
659                        return tex_round_xn_over_d(dw, e, scaling_factor);
660                    }
661                }
662            }
663        }
664    }
665    return 0;
666}
667
668scaled tex_kern_shrink(halfword p)
669{
670    scaled w = kern_amount(p) ;
671    if (w)  {
672        halfword l = lmt_packaging_state.previous_char_ptr;
673        if (l && node_type(l) == glyph_node && tex_has_glyph_expansion(l)) {
674            halfword m = lmt_font_state.adjust_shrink;
675            if (m > 0) {
676                scaled e = tex_char_cf_from_font(glyph_font(l), glyph_character(l));
677                if (e > 0) {
678                    scaled dw = tex_round_xn_over_d(w, scaling_factor - m, scaling_factor) - w;
679                    if (dw > 0) {
680                        return tex_round_xn_over_d(dw, e, scaling_factor);
681                    }
682                }
683            }
684        }
685    }
686    return 0;
687}
688
689/*tex 
690
691    We only need to set the factor when we have a non-zero kern and a following glyph that permits 
692    it being set. We never get here when |ratio| is zero.
693
694*/
695
696static void tex_aux_set_kern_expansion(halfword p, halfword ratio)
697{
698    if (kern_amount(p))  {
699        halfword glyph = lmt_packaging_state.previous_char_ptr;
700        if (glyph && node_type(glyph) == glyph_node && tex_has_glyph_expansion(glyph)) {
701            halfword fnt = glyph_font(glyph);
702            halfword chr = glyph_character(glyph);
703            if (ratio > 0) {
704                scaled expansion = tex_char_ef_from_font(fnt, chr);
705                if (expansion > 0) { 
706                    halfword stretch = lmt_font_state.adjust_stretch;
707                    if (stretch > 0) {
708                        kern_expansion(p) = tex_ext_xn_over_d(ratio * expansion, stretch, scaling_factor);
709                    }
710                }
711            } else if (ratio < 0) {
712                scaled compression = tex_char_cf_from_font(fnt, chr);
713                if (compression > 0) { 
714                    halfword shrink = lmt_font_state.adjust_shrink;
715                    if (shrink > 0) {
716                        kern_expansion(p) = tex_ext_xn_over_d(ratio * compression, shrink, scaling_factor);
717                    }
718                }
719            } else { 
720                /* already catched */
721            }
722        }
723    }
724}
725
726static void tex_aux_set_glyph_expansion(halfword p, int ratio)
727{
728    switch (node_type(p)) {
729        case glyph_node:
730            if (tex_has_glyph_expansion(p)) {
731                if (ratio > 0) {
732                    halfword fnt = glyph_font(p);
733                    halfword chr = glyph_character(p);
734                    scaled expansion = tex_char_ef_from_font(fnt, chr);
735                    if (expansion > 0) { 
736                        halfword stretch = lmt_font_state.adjust_stretch;
737                        if (stretch > 0) {
738                            glyph_expansion(p) = tex_ext_xn_over_d(ratio * expansion, stretch, scaling_factor);
739                        }
740                    }
741                } else if (ratio < 0) {
742                    halfword fnt = glyph_font(p);
743                    halfword chr = glyph_character(p);
744                    scaled compression = tex_char_cf_from_font(fnt, chr);
745                    if (compression > 0) { 
746                        halfword shrink = lmt_font_state.adjust_shrink;
747                        if (shrink > 0) {
748                            glyph_expansion(p) = tex_ext_xn_over_d(ratio * compression, shrink, scaling_factor);
749                        }
750                    }
751                }
752            }
753            break;
754        case disc_node:
755            {
756                halfword r = disc_pre_break_head(p);
757                while (r) {
758                    if (node_type(r) == glyph_node) {
759                        tex_aux_set_glyph_expansion(r, ratio);
760                    }
761                    r = node_next(r);
762                }
763                r = disc_post_break_head(p);
764                while (r) {
765                    if (node_type(r) == glyph_node) {
766                        tex_aux_set_glyph_expansion(r, ratio);
767                    }
768                    r = node_next(r);
769                }
770                r = disc_no_break_head(p);
771                while (r) {
772                    if (node_type(r) == glyph_node) {
773                        tex_aux_set_glyph_expansion(r, ratio);
774                    }
775                    r = node_next(r);
776                }
777                break;
778            }
779        default:
780            tex_normal_error("font expansion", "invalid node type");
781            break;
782    }
783}
784
785scaled tex_left_marginkern(halfword p)
786{
787    while (p && node_type(p) == glue_node) {
788        p = node_next(p);
789    }
790    if (p && node_type(p) == kern_node && node_subtype(p) == left_margin_kern_subtype) {
791        return kern_amount(p);
792    } else  {
793        return 0;
794    }
795}
796
797scaled tex_right_marginkern(halfword p)
798{
799    if (p) {
800        p = tex_tail_of_node_list(p);
801        /*tex
802            There can be a leftskip, rightskip, penalty and yes, also a disc node with a nesting
803            node that points to glue spec ... and we don't want to analyze that messy lot.
804        */
805        while (p) {
806            switch(node_type(p)) {
807                case glue_node:
808                    /*tex We backtrack over glue. */
809                    p = node_prev(p);
810                    break;
811                case kern_node:
812                    if (node_subtype(p) == right_margin_kern_subtype) {
813                        return kern_amount(p);
814                    } else {
815                        return 0;
816                    }
817                case disc_node:
818                    /*tex
819                        Officially we should look in the replace but currently protrusion doesn't
820                        work anyway with |foo\discretionary {} {} {bar-} | (no following char) so we
821                        don't need it now.
822                    */
823                    p = node_prev(p);
824                    if (p && node_type(p) == kern_node && node_subtype(p) == right_margin_kern_subtype) {
825                        return kern_amount(p);
826                    } else {
827                        return 0;
828                    }
829                default:
830                    return 0;
831            }
832        }
833    }
834    return 0;
835}
836
837/*tex
838
839    Character protrusion is something we inherited from \PDFTEX\ and the next helper calculates
840    the extend. Is this |last_*_char| logic still valid?
841
842*/
843
844scaled tex_char_protrusion(halfword p, int side)
845{
846    if (side == left_margin_kern_subtype) {
847        lmt_packaging_state.last_leftmost_char = null;
848    } else {
849        lmt_packaging_state.last_rightmost_char = null;
850    }
851    if (! p || node_type(p) != glyph_node || tex_has_glyph_option(p, glyph_option_no_protrusion)) {
852        return 0;
853    } else if (side == left_margin_kern_subtype) {
854        lmt_packaging_state.last_leftmost_char = p;
855        return tex_char_left_protrusion_from_glyph(p);
856    } else {
857        lmt_packaging_state.last_rightmost_char = p;
858        return tex_char_right_protrusion_from_glyph(p);
859    }
860}
861
862/*tex
863
864    Here we prepare for |hpack|, which is place where we do font substituting when font expansion
865    is being used.
866
867*/
868
869int tex_ignore_math_skip(halfword p)
870{
871    if (math_skip_mode_par == math_skip_only_when_skip) {
872        if (node_subtype(p) == end_inline_math) {
873            if (tex_math_skip_boundary(node_next(p))) {
874                return 0;
875            }
876        } else {
877            if (tex_math_skip_boundary(node_prev(p))) {
878                return 0;
879            }
880        }
881    } else if (math_skip_mode_par == math_skip_only_when_no_skip) {
882        if (node_subtype(p) == end_inline_math) {
883            if (! tex_math_skip_boundary(node_next(p))) {
884                return 0;
885            }
886        } else {
887            if (! tex_math_skip_boundary(node_prev(p))) {
888                return 0;
889            }
890        }
891    } else {
892        return 0;
893    }
894    tex_reset_math_glue_to_zero(p);
895    return 1;
896}
897
898# define fix_int(val,min,max) (val < min ? min : (val > max ? max : val))
899
900static inline halfword tex_aux_used_order(halfword *total)
901{
902    if (total[filll_glue_order]) {
903        return filll_glue_order;
904    } else if (total[fill_glue_order]) {
905        return fill_glue_order;
906    } else if (total[fil_glue_order]) {
907        return fil_glue_order;
908    } else if (total[fi_glue_order]) {
909        return fi_glue_order;
910    } else {
911        return normal_glue_order;
912    }
913}
914
915/*tex
916
917    The original code mentions: \quotation {Transfer node |p| to the adjustment list. Although node
918    |q| is not necessarily the immediate predecessor of node |p|, it always points to some node in
919    the list preceding |p|. Thus, we can delete nodes by moving |q| when necessary. The algorithm
920    takes linear time, and the extra computation does not intrude on the inner loop unless it is
921    necessary to make a deletion.}. The trick used is the following:
922
923    \starttyping
924    q = r + list_offset;
925    node_next(q) = p;
926    ....
927            while (node_next(q) != p) {
928                q = node_next(q);
929            }
930    \stoptyping
931
932    This list offset points to the memory slot in the node and it happens that the next pointer
933    takes the same subfield as the normal next pointer (these are actually offsets in an array of
934    memorywords). This kind of neat trickery is needed because there are only forward linked lists,
935    but we can do it differently and thereby also use the normal list pointer. We need a bit more
936    checking but in the end we have a better abstraction.
937
938*/
939
940/*tex 
941    For now we have this here but maybe it makes sense to move the adjust migration to the adjust 
942    module (tracing and such). It's a bit fyzzy code anyway. 
943*/
944
945static inline void tex_aux_promote_pre_migrated(halfword r, halfword p)
946{
947    halfword pa = box_pre_adjusted(p);
948    halfword pm = box_pre_migrated(p);
949    if (pa) {
950        if (lmt_packaging_state.pre_adjust_tail) {
951            lmt_packaging_state.pre_adjust_tail = tex_append_adjust_list(pre_adjust_head, lmt_packaging_state.pre_adjust_tail, pa, "promote");
952        } else if (box_pre_adjusted(r)) {
953            tex_couple_nodes(box_pre_adjusted(r), pa);
954        } else {
955            box_pre_adjusted(r) = pa;
956        }
957        box_pre_adjusted(p) = null;
958    }
959    if (pm) {
960        if (lmt_packaging_state.pre_migrate_tail) {
961            tex_couple_nodes(lmt_packaging_state.pre_migrate_tail, pm);
962            lmt_packaging_state.pre_migrate_tail = tex_tail_of_node_list(pm);
963        } else {
964            /* here we prepend pm to rm */
965            halfword rm = box_pre_migrated(r);
966            if (rm) {
967                tex_couple_nodes(pm, rm);
968            }
969            box_pre_migrated(r) = pm;
970        }
971        box_pre_migrated(p) = null;
972    }
973}
974
975static inline void tex_aux_promote_post_migrated(halfword r, halfword p)
976{
977    halfword pa = box_post_adjusted(p);
978    halfword pm = box_post_migrated(p);
979    if (pa) {
980        if (lmt_packaging_state.post_adjust_tail) {
981            lmt_packaging_state.post_adjust_tail = tex_append_adjust_list(post_adjust_head, lmt_packaging_state.post_adjust_tail, pa, "promote");
982        } else if (box_post_adjusted(r)) {
983            tex_couple_nodes(box_post_adjusted(r), pa);
984        } else {
985            box_post_adjusted(r) = pa;
986        }
987        box_post_adjusted(p) = null;
988    }
989    if (box_except(p)) {
990        lmt_packaging_state.except = box_except(p);
991    }
992    if (pm) {
993        if (lmt_packaging_state.post_migrate_tail) {
994            tex_couple_nodes(lmt_packaging_state.post_migrate_tail, pm);
995            lmt_packaging_state.post_migrate_tail = tex_tail_of_node_list(pm);
996        } else {
997            /* here we append pm to rm */
998            halfword rm = box_post_migrated(r);
999            if (rm) {
1000                tex_couple_nodes(tex_tail_of_node_list(rm), pm);
1001            } else {
1002                box_post_migrated(r) = pm;
1003            }
1004        }
1005        box_post_migrated(p) = null;
1006    }
1007}
1008
1009static inline halfword tex_aux_post_migrate(halfword r, halfword p)
1010{
1011    halfword n = p;
1012    halfword nn = node_next(p);
1013    halfword pm = box_post_migrated(r);
1014    if (p == box_list(r)) {
1015        box_list(r) = nn;
1016        if (nn) {
1017            node_prev(nn) = null;
1018        }
1019    } else {
1020        tex_couple_nodes(node_prev(p), nn);
1021    }
1022    if (pm) {
1023        tex_couple_nodes(tex_tail_of_node_list(pm), n);
1024    } else {
1025        box_post_migrated(r) = n;
1026    }
1027    node_next(n) = null;
1028    p = nn;
1029    return p;
1030}
1031
1032static inline halfword tex_aux_normal_migrate(halfword r, halfword p)
1033{
1034    halfword n = p;
1035    halfword nn = node_next(p);
1036    if (p == box_list(r)) {
1037        box_list(r) = nn;
1038        if (nn) {
1039            node_prev(nn) = null;
1040        }
1041    } else {
1042        tex_couple_nodes(node_prev(p), nn);
1043    }
1044    tex_couple_nodes(lmt_packaging_state.post_migrate_tail, n);
1045    lmt_packaging_state.post_migrate_tail = n;
1046    node_next(n) = null;
1047    p = nn;
1048    return p;
1049}
1050
1051static void tex_aux_append_diagnostic_rule(halfword box, halfword rule)
1052{
1053    halfword n = box_list(box);
1054    if (n) {
1055        /* the caller actually knows tail */
1056        halfword t = tex_tail_of_node_list(n);
1057        halfword c = t; 
1058        while (c && node_type(c) == glue_node) {
1059            switch (node_subtype(c)) { 
1060                case par_fill_right_skip_glue:
1061                case par_init_right_skip_glue:
1062                case right_skip_glue:
1063                case right_hang_skip_glue:
1064                    c = node_prev(c); 
1065                    break;
1066                default:
1067                    goto DONE; 
1068            }
1069        }
1070      DONE:
1071        if (c) { 
1072            n = node_next(c);
1073            if (n) { 
1074                tex_couple_nodes(rule, n);
1075            }
1076        } else { 
1077            c = t; 
1078        }
1079        tex_couple_nodes(c, rule);
1080    } else { 
1081        box_list(box) = rule;
1082    }
1083}
1084
1085void tex_repack(halfword p, scaled w, int m)
1086{
1087    if (p) {
1088        halfword tmp; 
1089        switch (node_type(p)) { 
1090            case hlist_node:
1091                tmp = tex_hpack(box_list(p), w, m, box_dir(p), holding_none_option, box_limit_none);
1092                break;
1093            case vlist_node: 
1094                tmp = tex_vpack(box_list(p), w, m > packing_additional ? packing_additional : m, max_dimension, box_dir(p), holding_none_option, NULL);
1095                break;
1096            default: 
1097                return;
1098        }
1099        box_width(p) = box_width(tmp);
1100        box_height(p) = box_height(tmp);
1101        box_depth(p) = box_depth(tmp);
1102        box_glue_set(p) = box_glue_set(tmp);
1103        box_glue_order(p) = box_glue_order(tmp);
1104        box_glue_sign(p) = box_glue_sign(tmp);
1105        box_list(tmp) = null;
1106        tex_flush_node(tmp);
1107    }
1108}
1109
1110// Not ok. For now we accept some drift and assume it averages out. Just   
1111// for fun we could actually store it in the glue set field afterwards. 
1112// 
1113// { 
1114//     halfword drift = scaledround(wd) - ws;
1115//     if (drift < 0) {
1116//         d -= (double) drift; 
1117//         wd -= (double) drift; 
1118//     }
1119// } 
1120
1121/*tex 
1122    When |limitate| equals the node type we indeed limitate. Otherwise we freeze. 
1123*/
1124
1125void tex_freeze(halfword p, int recurse, int limitate, halfword factor) 
1126{
1127    if (p) {
1128        switch (node_type(p)) { 
1129            case hlist_node:
1130                {
1131                    halfword c = box_list(p);
1132                    double set = (double) box_glue_set(p);
1133                    halfword order = box_glue_order(p);
1134                    singleword sign = box_glue_sign(p);
1135                    if (factor > 0) { 
1136                        set *= factor * 0.001;
1137                    } else if (factor < 0) {
1138                        double max = - factor * 0.001; 
1139                        if (set > max) {
1140                            set = max;
1141                        }
1142                    }
1143                    while (c) {
1144                        switch (node_type(c)) {
1145                            case glue_node:
1146                                switch (sign) {
1147                                    case stretching_glue_sign:
1148                                        if (glue_stretch_order(c) == order) {
1149                                            glue_amount(c) += limitate == hlist_node ? glue_stretch(c) : scaledround(glue_stretch(c) * set);
1150                                        }
1151                                        break;
1152                                    case shrinking_glue_sign:
1153                                        if (glue_shrink_order(c) == order) {
1154                                            glue_amount(c) -= scaledround(glue_shrink(c) * set);
1155                                        }
1156                                        break;
1157                                }
1158                                glue_stretch(c) = 0;
1159                                glue_shrink(c) = 0;
1160                                glue_stretch_order(c) = 0;
1161                                glue_shrink_order(c) = 0;
1162                                break;
1163                            case hlist_node:
1164                            case vlist_node:
1165                                if (recurse) {
1166                                    tex_freeze(c, recurse, limitate, factor);
1167                                }
1168                                break;
1169                            case math_node:
1170                                switch (sign) {
1171                                    case stretching_glue_sign:
1172                                        if (math_stretch_order(c) == order) {
1173                                            math_amount(c) += limitate == hlist_node ? math_stretch(c) : scaledround(math_stretch(c) * set);
1174                                        }
1175                                        break;
1176                                    case shrinking_glue_sign:
1177                                        if (math_shrink_order(c) == order) {
1178                                            math_amount(c) += scaledround(math_shrink(c) * set);
1179                                        }
1180                                        break;
1181                                }
1182                                math_stretch(c) = 0;
1183                                math_shrink(c) = 0;
1184                                math_stretch_order(c) = 0;
1185                                math_shrink_order(c) = 0;
1186                                break;
1187                            default: 
1188                                break;
1189                        }
1190                        c = node_next(c);
1191                    }
1192                    box_glue_set(p) = 0;
1193                    box_glue_order(p) = 0;
1194                    box_glue_sign(p) = 0;
1195                }
1196                break;
1197            case vlist_node: 
1198                {
1199                    halfword c = box_list(p);
1200                    double set = (double) box_glue_set(p);
1201                    halfword order = box_glue_order(p);
1202                    singleword sign = box_glue_sign(p);
1203                    if (factor > 0) { 
1204                        set *= factor * 0.001;
1205                    } else if (factor < 0) {
1206                        double max = - factor * 0.001; 
1207                        if (set > max) {
1208                            set = max;
1209                        }
1210                    }
1211                    while (c) {
1212                        switch (node_type(c)) {
1213                            case glue_node:
1214                                switch (sign) {
1215                                    case stretching_glue_sign:
1216                                        if (glue_stretch_order(c) == order) {
1217                                                glue_amount(c) += limitate == vlist_node ? glue_stretch(c) : scaledround(glue_stretch(c) * set);
1218                                        }
1219                                        break;
1220                                    case shrinking_glue_sign:
1221                                        if (glue_shrink_order(c) == order) {
1222                                            glue_amount(c) -= scaledround(glue_shrink(c) * set);
1223                                        }
1224                                        break;
1225                                }
1226                                glue_stretch(c) = 0;
1227                                glue_shrink(c) = 0;
1228                                glue_stretch_order(c) = 0;
1229                                glue_shrink_order(c) = 0;
1230                                break;
1231                            case hlist_node:
1232                            case vlist_node:
1233                                if (recurse) {
1234                                    tex_freeze(c, recurse, limitate, factor);
1235                                }
1236                                break;
1237                            default: 
1238                                break;
1239                        }
1240                        c = node_next(c);
1241                    }
1242                    box_glue_set(p) = 0;
1243                    box_glue_order(p) = 0;
1244                    box_glue_sign(p) = 0;
1245                }
1246                break;
1247            default: 
1248                return;
1249        }
1250    }
1251}
1252
1253static inline halfword tex_aux_migrate_decouple(halfword head, halfword current, halfword next, halfword *first, halfword *last)
1254{
1255    halfword prev = node_prev(current);
1256    tex_uncouple_node(current);
1257    if (current == head) {
1258        node_prev(next) = null;
1259        head = next;
1260    } else {
1261        tex_try_couple_nodes(prev, next);
1262    }
1263    if (*first) {
1264        tex_couple_nodes(*last, current);
1265    } else {
1266        *first = current;
1267    }
1268    *last = current;
1269    return head;
1270}
1271
1272static halfword tex_aux_migrate_locate(halfword head, halfword *first, halfword *last, int inserts, int marks)
1273{
1274    halfword current = head;
1275    while (current) {
1276        halfword next = node_next(current);
1277        switch (node_type(current)) {
1278            case vlist_node:
1279            case hlist_node:
1280                {
1281                    halfword list = box_list(current);
1282                    if (list) {
1283                        box_list(current) = tex_aux_migrate_locate(list, first, last, inserts, marks);
1284                    }
1285                    break;
1286                }
1287            case insert_node:
1288                {
1289                    if (inserts) {
1290                        halfword list;
1291                        head = tex_aux_migrate_decouple(head, current, next, first, last);
1292                        list = insert_list(current);
1293                        if (list) {
1294                            insert_list(current) = tex_aux_migrate_locate(list, first, last, inserts, marks);
1295                        }
1296                    }
1297                    break;
1298                }
1299            case mark_node:
1300                {
1301                    if (marks) {
1302                        head = tex_aux_migrate_decouple(head, current, next, first, last);
1303                    }
1304                    break;
1305                }
1306            default:
1307                break;
1308        }
1309        current = next;
1310    }
1311    return head;
1312}
1313
1314void tex_migrate(halfword head, halfword *first, halfword *last, int inserts, int marks)
1315{
1316    if (head) {
1317        halfword current = head;
1318        while (current) {
1319            switch (node_type(current)) {
1320                case hlist_node:
1321                case vlist_node:
1322                    {
1323                        halfword list = box_list(current);
1324                        if (list) {
1325                            box_list(current) = tex_aux_migrate_locate(list, first, last, inserts, marks);
1326                        }
1327                        break;
1328                    }
1329                 case insert_node:
1330                     if (inserts) {
1331                         halfword list = insert_list(current);
1332                         if (list) {
1333                            insert_list(current) = tex_aux_migrate_locate(list, first, last, inserts, marks);
1334                         }
1335                         break;
1336                     }
1337            }
1338            current = node_next(current);
1339        }
1340    }
1341}
1342
1343void tex_limit(halfword p) 
1344{
1345    /* 
1346        For now we only handle stretch. This is not watertight because when we max some, the others
1347        actually can becoem relative smaller. Of course we can also freeze and then recalculate. 
1348    */
1349    if (p) { 
1350        halfword c = box_list(p);
1351        double set = (double) box_glue_set(p);
1352        halfword order = box_glue_order(p);
1353        singleword sign = box_glue_sign(p);
1354        int limit = set > 1.0 && order == normal_glue_order; 
1355        if (limit) {
1356            long long nonfrozen = 0;
1357            long long frozen = 0;
1358            while (c) {
1359                if (node_type(c) == glue_node) {
1360                    switch (sign) {
1361                        case stretching_glue_sign:
1362                            if (glue_stretch_order(c) == order) {
1363                                if (tex_has_glue_option(c, glue_option_limit)) {
1364                                    frozen += glue_stretch(c);
1365                                } else {
1366                                    nonfrozen += glue_stretch(c);
1367                                }
1368                            }
1369                            break;
1370                        case shrinking_glue_sign:
1371                            if (glue_shrink_order(c) == order) {
1372                                if (tex_has_glue_option(c, glue_option_limit)) {
1373                                    frozen -= glue_shrink(c);
1374                                } else { 
1375                                    nonfrozen -= glue_shrink(c);
1376                                }
1377                            }
1378                            break;
1379                    }
1380                }
1381                c = node_next(c);
1382            }
1383            if (nonfrozen) { /* was: frozen, needs checking */
1384                set = (double) (set * (frozen + nonfrozen) - frozen) / (double) nonfrozen; 
1385            } else { 
1386                limit = 0;
1387            }
1388        }
1389        c = box_list(p);
1390        while (c) {
1391            if (node_type(c) == glue_node) {
1392                switch (sign) {
1393                    case stretching_glue_sign:
1394                        if (glue_stretch_order(c) == order) {
1395                            if (limit && tex_has_glue_option(c, glue_option_limit)) {
1396                                glue_amount(c) += glue_stretch(c);
1397                            } else {
1398                                glue_amount(c) += scaledround(glue_stretch(c) * set);
1399                            }
1400                        }
1401                        break;
1402                    case shrinking_glue_sign:
1403                        if (glue_shrink_order(c) == order) {
1404                            if (limit && tex_has_glue_option(c, glue_option_limit)) {
1405                                glue_amount(c) -= glue_shrink(c);
1406                            } else { 
1407                                glue_amount(c) -= scaledround(glue_shrink(c) * set);
1408                            }
1409                        }
1410                        break;
1411                }
1412                glue_stretch(c) = 0;
1413                glue_shrink(c) = 0;
1414                glue_stretch_order(c) = 0;
1415                glue_shrink_order(c) = 0;
1416            }
1417            c = node_next(c);
1418        }
1419        box_glue_set(p) = 0;
1420        box_glue_order(p) = 0;
1421        box_glue_sign(p) = 0;
1422    }
1423}
1424
1425scaled tex_stretch(halfword p)
1426{
1427    scaled stretch = 0;
1428    if (p) {
1429        switch (node_type(p)) { 
1430            case hlist_node:
1431            case vlist_node:
1432                if (box_glue_sign(p) == stretching_glue_sign) { 
1433                    halfword c = box_list(p);
1434                    halfword order = box_glue_order(p);
1435                    while (c) {
1436                        switch (node_type(c)) {
1437                            case glue_node:
1438                                if (glue_stretch_order(c) == order) {
1439                                    stretch += glue_stretch(c);
1440                                }
1441                                break;
1442                            case math_node:
1443                                if (math_stretch_order(c) == order) {
1444                                    stretch += math_stretch(c);
1445                                }
1446                                break;
1447                        }
1448                        c = node_next(c);
1449                    }
1450                }
1451                break;
1452        }
1453    }
1454    return stretch;
1455}
1456
1457scaled tex_shrink(halfword p)
1458{
1459    scaled shrink = 0;
1460    if (p) {
1461        switch (node_type(p)) { 
1462            case hlist_node:
1463            case vlist_node:
1464                if (box_glue_sign(p) == shrinking_glue_sign) { 
1465                    halfword c = box_list(p);
1466                    halfword order = box_glue_order(p);
1467                    while (c) {
1468                        switch (node_type(c)) {
1469                            case glue_node:
1470                                if (glue_shrink_order(c) == order) {
1471                                    shrink += glue_shrink(c);
1472                                }
1473                                break;
1474                            case math_node:
1475                                if (math_shrink_order(c) == order) {
1476                                    shrink += math_shrink(c);
1477                                }
1478                                break;
1479                        }
1480                        c = node_next(c);
1481                    }
1482                }
1483                break;
1484        }
1485    }
1486    return shrink;
1487}
1488
1489halfword tex_hpack(halfword p, scaled target, int method, singleword pack_direction, int retain, int limit)
1490{
1491    halfword tail = null;
1492    scaled height = 0;
1493    scaled depth = 0;
1494    scaled width = 0;
1495    scaled excess = 0;
1496    singleword hpack_dir = pack_direction == direction_unknown ?(singleword) text_direction_par : pack_direction;
1497    int disc_level = 0;
1498    int has_uleader = 0;
1499    halfword pack_interrupt[8];
1500    scaled font_stretch = 0;
1501    scaled font_shrink = 0;
1502    int adjust_spacing = adjust_spacing_off;
1503    int has_limit = box_limit_mode_hlist ? 1 : (limit == box_limit_line && box_limit_mode_line ? 1 : -1);
1504    /*tex the box node that will be returned */
1505    halfword result = tex_new_node(hlist_node, unknown_list);
1506    box_dir(result) = hpack_dir;
1507    lmt_packaging_state.last_badness = 0;
1508    lmt_packaging_state.last_overshoot = 0;
1509    switch(method) { 
1510        case packing_linebreak:
1511            method = packing_expanded;
1512            /* fall through, later we'll come back here: */
1513        case packing_substitute:
1514            adjust_spacing = tex_checked_font_adjust(
1515                lmt_linebreak_state.adjust_spacing,
1516                lmt_linebreak_state.adjust_spacing_step,
1517                lmt_linebreak_state.adjust_spacing_shrink,
1518                lmt_linebreak_state.adjust_spacing_stretch
1519            );
1520            break;
1521        default:
1522            adjust_spacing = tex_checked_font_adjust(
1523                adjust_spacing_par,
1524                adjust_spacing_step_par,
1525                adjust_spacing_shrink_par,
1526                adjust_spacing_stretch_par
1527            );
1528            break;
1529    }
1530    box_list(result) = p;
1531    switch (method) { 
1532        case packing_expanded:
1533            /*tex Why not always: */
1534            lmt_packaging_state.previous_char_ptr = null;
1535            break;
1536        case packing_adapted:
1537            if (target > scaling_factor) { 
1538                target = scaling_factor;
1539            } else if (target < -scaling_factor) { 
1540                target = -scaling_factor;
1541            }
1542    }
1543    for (int i = normal_glue_order; i <= filll_glue_order; i++) {
1544        lmt_packaging_state.total_stretch[i] = 0;
1545        lmt_packaging_state.total_shrink[i] = 0;
1546    }
1547    /*tex
1548
1549        Examine node |p| in the hlist, taking account of its effect on the dimensions of the new
1550        box, or moving it to the adjustment list; then advance |p| to the next node. For disc
1551        node we enter a level so we don't use recursion.
1552
1553        In other engines there is an optimization for glyph runs but here we use just one switch
1554        for everything. The performance hit is neglectable. So the comment \quotation {Incorporate
1555        character dimensions into the dimensions of the hbox that will contain~it, then move to
1556        the next node.} no longer applies. In \LUATEX\ ligature building, kerning and hyphenation
1557        are decoupled so comments about inner loop and performance no longer make sense here.
1558
1559    */
1560    while (p) {
1561        switch (node_type(p)) {
1562            case glyph_node:
1563                {
1564                    scaledwhd whd;
1565                    if (adjust_spacing) {
1566                        switch (method) {
1567                            case packing_expanded:
1568                                {
1569                                    lmt_packaging_state.previous_char_ptr = p;
1570                                    font_stretch += tex_char_stretch(p);
1571                                    font_shrink += tex_char_shrink(p);
1572                                    break;
1573                                }
1574                            case packing_substitute:
1575                                lmt_packaging_state.previous_char_ptr = p;
1576                                if (lmt_packaging_state.font_expansion_ratio != 0) {
1577                                    tex_aux_set_glyph_expansion(p, lmt_packaging_state.font_expansion_ratio);
1578                                }
1579                                break;
1580                        }
1581                    }
1582                    whd = tex_glyph_dimensions_ex(p);
1583                    width += whd.wd;
1584                    if (whd.ht > height) {
1585                        height = whd.ht;
1586                    }
1587                    if (whd.dp > depth) {
1588                        depth = whd.dp;
1589                    }
1590                    break;
1591                }
1592            case hlist_node:
1593            case vlist_node:
1594                {
1595                    /*tex
1596
1597                        Incorporate box dimensions into the dimensions of the hbox that will contain
1598                        it.
1599
1600                    */
1601                    halfword shift = box_shift_amount(p);
1602                    scaledwhd whd = tex_pack_dimensions(p);
1603                    width += whd.wd;
1604                    if (whd.ht - shift > height) {
1605                        height = whd.ht - shift;
1606                    }
1607                    if (whd.dp + shift > depth) {
1608                        depth = whd.dp + shift;
1609                    }
1610                    tex_aux_promote_pre_migrated(result, p);
1611                    tex_aux_promote_post_migrated(result, p);
1612                    break;
1613                }
1614            case unset_node:
1615                width += box_width(p);
1616                if (box_height(p) > height) {
1617                    height = box_height(p);
1618                }
1619                if (box_depth(p) > depth) {
1620                    depth = box_depth(p);
1621                }
1622                /*tex No promotions here. */
1623                break;
1624            case rule_node:
1625                /*tex
1626
1627                    The code here implicitly uses the fact that running dimensions are indicated
1628                    by |null_flag|, which will be ignored in the calculations because it is a
1629                    highly negative number.
1630
1631                */
1632                width += rule_width(p);
1633                if (rule_height(p) > height) {
1634                    height = rule_height(p);
1635                }
1636                if (rule_depth(p) > depth) {
1637                    depth = rule_depth(p);
1638                }
1639                break;
1640            case glue_node:
1641                /*tex Incorporate glue into the horizontal totals. Can this overflow? */
1642                {
1643                    switch (method) { 
1644                        case packing_adapted:
1645                            if (target < 0) {
1646                                if (glue_shrink_order(p) == normal_glue_order) {                                   
1647                                    glue_amount(p) -= scaledround(-0.001 * target * (double) glue_shrink(p));
1648                                }
1649                            } else if (target > 0) {
1650                                if (glue_stretch_order(p) == normal_glue_order) {
1651                                    glue_amount(p) += scaledround( 0.001 * target * (double) glue_stretch(p));
1652                                }
1653                            }
1654                            width += glue_amount(p);
1655                            glue_shrink_order(p) = normal_glue_order;
1656                            glue_shrink(p) = 0;
1657                            glue_stretch_order(p) = normal_glue_order;
1658                            glue_stretch(p) = 0;
1659                            break;
1660                        default:
1661                            width += glue_amount(p);
1662                            lmt_packaging_state.total_stretch[glue_stretch_order(p)] += glue_stretch(p);
1663                            lmt_packaging_state.total_shrink[glue_shrink_order(p)] += glue_shrink(p);
1664                    }
1665                    if (is_leader(p)) {
1666                        halfword gl = glue_leader_ptr(p);
1667                        scaled ht = 0;
1668                        scaled dp = 0;
1669                        switch (node_type(gl)) {
1670                            case hlist_node:
1671                            case vlist_node:
1672                                ht = box_height(gl);
1673                                dp = box_depth(gl);
1674                                break;
1675                            case rule_node:
1676                                ht = rule_height(gl);
1677                                dp = rule_depth(gl);
1678                                break;
1679                            /* how about glyph_node */
1680                        }
1681                        if (ht > height) {
1682                            height = ht;
1683                        }
1684                        if (dp > depth) {
1685                            depth = dp;
1686                        }
1687                        if (node_subtype(p) == u_leaders) {
1688                            has_uleader = 1;
1689                        }
1690                    }
1691                    if (! has_limit && tex_has_glue_option(p, glue_option_limit)) {
1692                        has_limit = 1;
1693                    }
1694                    break;
1695                }
1696            case kern_node:
1697                if (adjust_spacing == adjust_spacing_full && node_subtype(p) == font_kern_subtype) {
1698                    switch (method) {
1699                        case packing_expanded:
1700                            {
1701                                font_stretch += tex_kern_stretch(p);
1702                                font_shrink += tex_kern_shrink(p);
1703                                break;
1704                            }
1705                        case packing_substitute:
1706                            if (lmt_packaging_state.font_expansion_ratio != 0) {
1707                                tex_aux_set_kern_expansion(p, lmt_packaging_state.font_expansion_ratio);
1708                            }
1709                            break;
1710                    }
1711                }
1712                width += tex_kern_dimension_ex(p);
1713                break;
1714            case disc_node:
1715                if (adjust_spacing) {
1716                    switch (method) {
1717                        case packing_expanded:
1718                            /*tex
1719                                Won't give this issues with complex discretionaries as we don't
1720                                do the |packing_expand| here? I need to look into this!
1721                            */
1722                            break;
1723                        case packing_substitute:
1724                            if (lmt_packaging_state.font_expansion_ratio != 0) {
1725                                tex_aux_set_glyph_expansion(p, lmt_packaging_state.font_expansion_ratio);
1726                            }
1727                            break;
1728                    }
1729                }
1730                if (disc_no_break_head(p)) {
1731                    pack_interrupt[disc_level] = node_next(p);
1732                    ++disc_level;
1733                    p = disc_no_break(p);
1734                }
1735                break;
1736            case math_node:
1737                if (tex_math_glue_is_zero(p) || tex_ignore_math_skip(p)) {
1738                    width += math_surround(p);
1739                } else {
1740                    width += math_amount(p);
1741                    lmt_packaging_state.total_stretch[math_stretch_order(p)] += math_stretch(p);
1742                    lmt_packaging_state.total_shrink[math_shrink_order(p)] += math_shrink(p);
1743                }
1744                break;
1745            case dir_node:
1746                break;
1747            case insert_node:
1748                if (retain_inserts(retain)) {
1749                    break;
1750                } else if (lmt_packaging_state.post_migrate_tail) {
1751                    p = tex_aux_normal_migrate(result, p);
1752                    /*tex Here |q| stays as it is and we're already at next. */
1753                    continue;
1754                } else if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_insert)) {
1755                    halfword l = insert_list(p);
1756                    p = tex_aux_post_migrate(result, p);
1757                    while (l) {
1758                        l = node_type(l) == insert_node ? tex_aux_post_migrate(result, l) : node_next(l);
1759                    }
1760                    /*tex Here |q| stays as it is and we're already at next. */
1761                    continue;
1762                } else {
1763                    /*tex Nothing done, so we move on. */
1764                    break;
1765                }
1766            case mark_node:
1767                if (retain_marks(retain)) {
1768                    break;
1769                } else if (lmt_packaging_state.post_migrate_tail) {
1770                    p = tex_aux_normal_migrate(result, p);
1771                    /*tex Here |q| stays as it is and we're already at next. */
1772                    continue;
1773                } else if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_mark)) {
1774                    p = tex_aux_post_migrate(result, p);
1775                    /*tex Here |q| stays as it is and we're already at next. */
1776                    continue;
1777                } else {
1778                    /*tex Nothing done, so we move on. */
1779                    break;
1780                }
1781            case adjust_node:
1782                /*tex
1783                    We could combine this with migration code but adjust content actually is taken into account
1784                    as part of the flow (dimensions, penalties, etc).
1785                */
1786                if (adjust_list(p) && ! retain_adjusts(retain)) {
1787                    halfword next = node_next(p);
1788                    halfword current = p;
1789                    /*tex Remove from list: */
1790                    if (p == box_list(result)) {
1791                        box_list(result) = next;
1792                        if (next) {
1793                            node_prev(next) = null;
1794                        }
1795                    } else {
1796                        tex_couple_nodes(node_prev(p), next);
1797                    }
1798                    if (lmt_packaging_state.post_adjust_tail || lmt_packaging_state.pre_adjust_tail) {
1799                        tex_adjust_passon(result, current);
1800                    } else if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_adjust)) {
1801                        tex_adjust_attach(result, current);
1802                    }
1803                    p = next;
1804                    continue;
1805                } else {
1806                    break;
1807                }
1808            default:
1809                break;
1810        }
1811        /*
1812            This is kind of tricky: q is the pre-last pointer so we don't change it when we're
1813            inside a disc node. This way of keeping track of the last node is different from the
1814            previous engine.
1815        */
1816        if (disc_level > 0) {
1817            p = node_next(p);
1818            if (! p) {
1819                --disc_level;
1820                p = pack_interrupt[disc_level];
1821            }
1822        } else {
1823            tail = p;
1824            p = node_next(p);
1825        }
1826    }
1827    box_height(result) = height;
1828    box_depth(result) = depth;
1829    /*tex
1830        Determine the value of |width(r)| and the appropriate glue setting; then |return| or |goto
1831        common_ending|. When we get to the present part of the program, |x| is the natural width of
1832        the box being packaged.
1833    */
1834    switch (method) { 
1835        case packing_additional: 
1836            target += width;
1837            break;
1838        case packing_adapted: 
1839            target = width;
1840            break;
1841    }
1842    box_width(result) = target;
1843    excess = target - width;
1844    if (excess == 0) {
1845        box_glue_sign(result) = normal_glue_sign;
1846        box_glue_order(result) = normal_glue_order;
1847        box_glue_set(result) = 0.0;
1848        goto EXIT;
1849    } else if (excess > 0) {
1850        /*tex
1851            Determine horizontal glue stretch setting, then |return| or |goto common_ending|. If
1852            |hpack| is called with |m=cal_expand_ratio| we calculate |font_expand_ratio| and return
1853            without checking for overfull or underfull box.
1854        */
1855        halfword order = tex_aux_used_order(lmt_packaging_state.total_stretch);
1856        if ((method == packing_expanded) && (order == normal_glue_order) && (font_stretch > 0)) {
1857            lmt_packaging_state.font_expansion_ratio = tex_divide_scaled_n(excess, font_stretch, scaling_factor_double);
1858            goto EXIT;
1859        }
1860        box_glue_order(result) = order;
1861        box_glue_sign(result) = stretching_glue_sign;
1862        if (lmt_packaging_state.total_stretch[order]) {
1863            box_glue_set(result) = (glueratio) ((double) excess / lmt_packaging_state.total_stretch[order]);
1864        } else {
1865            /*tex There's nothing to stretch. */
1866            box_glue_sign(result) = normal_glue_sign;
1867            box_glue_set(result) = 0.0;
1868        }
1869        if (order == normal_glue_order && box_list(result)) {
1870            /*tex
1871                Report an underfull hbox and |goto common_ending|, if this box is sufficiently bad.
1872            */
1873            lmt_packaging_state.last_badness = tex_badness(excess, lmt_packaging_state.total_stretch[normal_glue_order]);
1874            if (lmt_packaging_state.last_badness > hbadness_par) {
1875                int what = badness_mode_nothing;
1876                if (hbadness_mode_par & badness_mode_loose) { 
1877                    what = badness_mode_loose;
1878                } else if ((hbadness_mode_par & badness_mode_underfull) && (lmt_packaging_state.last_badness > 100)) {
1879                    what = badness_mode_underfull;
1880                }
1881                if (what != badness_mode_nothing) {
1882                    int callback_id = lmt_callback_defined(hpack_quality_callback);
1883                    const char *verdict = what == badness_mode_underfull ? "underfull" : "loose"; // beware, this needs to be adapted to the configureable 99 
1884                    if (callback_id > 0) {
1885                        if (tail) { 
1886                            halfword rule = null;
1887                            lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "SdNddS->N",
1888                                verdict, 
1889                                lmt_packaging_state.last_badness,
1890                                result,
1891                                abs(lmt_packaging_state.pack_begin_line),
1892                                lmt_input_state.input_line,
1893                                tex_current_input_file_name(),
1894                                &rule
1895                            );
1896                            if (rule && rule != result) {
1897                                tex_aux_append_diagnostic_rule(result, rule);
1898                            }
1899                        }
1900                    } else {
1901                        tex_print_nlp();
1902                        tex_print_format(
1903                            "%l[package: %s \\hbox (badness %i)", 
1904                            verdict,
1905                            lmt_packaging_state.last_badness
1906                        );
1907                        goto COMMON_ENDING;
1908                    }
1909                }
1910            }
1911        }
1912        goto EXIT;
1913    } else {
1914        /*tex
1915            Determine horizontal glue shrink setting, then |return| or |goto common_ending|,
1916        */
1917        halfword order = tex_aux_used_order(lmt_packaging_state.total_shrink);
1918        if ((method == packing_expanded) && (order == normal_glue_order) && (font_shrink > 0)) {
1919            lmt_packaging_state.font_expansion_ratio = tex_divide_scaled_n(excess, font_shrink, scaling_factor_double);
1920            goto EXIT;
1921        }
1922        box_glue_order(result) = order;
1923        box_glue_sign(result) = shrinking_glue_sign;
1924        if (lmt_packaging_state.total_shrink[order]) {
1925            box_glue_set(result) = (glueratio) ((double) (-excess) / (double) lmt_packaging_state.total_shrink[order]);
1926        } else {
1927            /*tex There's nothing to shrink. */
1928            box_glue_sign(result) = normal_glue_sign;
1929            box_glue_set(result) = 0.0;
1930        }
1931        if (order == normal_glue_order && box_list(result)) {
1932            if (lmt_packaging_state.total_shrink[order] < -excess) {
1933                int overshoot = -excess - lmt_packaging_state.total_shrink[normal_glue_order];
1934                lmt_packaging_state.last_badness = scaling_factor_squared;
1935                lmt_packaging_state.last_overshoot = overshoot;
1936                /*tex Use the maximum shrinkage */
1937                box_glue_set(result) = 1.0;
1938                /*tex Report an overfull hbox and |goto common_ending|, if this box is sufficiently bad. */
1939                if (((overshoot > hfuzz_par) || (hbadness_par < 100)) && (hbadness_mode_par & badness_mode_overfull)) {
1940                    int callback_id = lmt_callback_defined(hpack_quality_callback);
1941                    halfword rule = null;
1942                    if (callback_id > 0) {
1943                        lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "SdNddS->N",
1944                            "overfull",
1945                            overshoot,
1946                            result,
1947                            abs(lmt_packaging_state.pack_begin_line),
1948                            lmt_input_state.input_line,
1949                            tex_current_input_file_name(),
1950                            &rule);
1951                    } else if (tail && overfull_rule_par > 0) {
1952                        rule = tex_new_rule_node(normal_rule_subtype);
1953                        rule_width(rule) = overfull_rule_par;
1954                    }
1955                    if (rule && rule != result) {
1956                        tex_aux_append_diagnostic_rule(result, rule);
1957                    }
1958                    if (callback_id == 0) {
1959                        tex_print_nlp();
1960                        tex_print_format("%l[package: overfull \\hbox (%p too wide)", overshoot);
1961                        goto COMMON_ENDING;
1962                    }
1963                }
1964            } else {
1965                /*tex Report a tight hbox and |goto common_ending|, if this box is sufficiently bad. */
1966                lmt_packaging_state.last_badness = tex_badness(-excess, lmt_packaging_state.total_shrink[normal_glue_order]);
1967                if (lmt_packaging_state.last_badness > hbadness_par && (hbadness_mode_par & badness_mode_tight)) {
1968                    int callback_id = lmt_callback_defined(hpack_quality_callback);
1969                    if (callback_id > 0) {
1970                        halfword rule = null;
1971                        lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "SdNddS->N",
1972                            "tight",
1973                            lmt_packaging_state.last_badness,
1974                            result,
1975                            abs(lmt_packaging_state.pack_begin_line),
1976                            lmt_input_state.input_line,
1977                            tex_current_input_file_name(),
1978                            &rule);
1979                        if (rule && rule != result) {
1980                            tex_aux_append_diagnostic_rule(result, rule);
1981                        }
1982                    } else {
1983                        tex_print_nlp();
1984                        tex_print_format("%l[package: tight \\hbox (badness %i)", lmt_packaging_state.last_badness);
1985                        goto COMMON_ENDING;
1986                    }
1987                }
1988            }
1989        }
1990        goto EXIT;
1991    }
1992  COMMON_ENDING:
1993    /*tex Finish issuing a diagnostic message for an overfull or underfull hbox. */
1994    if (lmt_page_builder_state.output_active) {
1995        tex_print_format(" has occurred while \\output is active]");
1996    } else if (lmt_packaging_state.pack_begin_line == 0) {
1997        tex_print_format(" detected at line %i]", lmt_input_state.input_line);
1998    } else if (lmt_packaging_state.pack_begin_line > 0) {
1999        tex_print_format(" in paragraph at lines %i--%i]", lmt_packaging_state.pack_begin_line, lmt_input_state.input_line);
2000    } else {
2001        tex_print_format(" in alignment at lines %i--%i]", -lmt_packaging_state.pack_begin_line, lmt_input_state.input_line);
2002    }
2003    tex_print_ln();
2004    lmt_print_state.font_in_short_display = null_font;
2005    if (tracing_full_boxes_par > 0) {
2006        halfword detail = show_node_details_par;
2007        show_node_details_par = tracing_full_boxes_par;
2008        tex_print_str("%l[package: result] ");
2009        tex_short_display(box_list(result));
2010        tex_print_ln();
2011        tex_begin_diagnostic();
2012        tex_show_box(result);
2013        tex_end_diagnostic();
2014        show_node_details_par = detail;
2015    }
2016  EXIT:
2017    if ((method == packing_expanded) && (lmt_packaging_state.font_expansion_ratio != 0)) {
2018        halfword list = box_list(result);
2019        box_list(result) = null;
2020        tex_flush_node(result);
2021        lmt_packaging_state.font_expansion_ratio = fix_int(lmt_packaging_state.font_expansion_ratio, -scaling_factor, scaling_factor);
2022        /*tex This nested call uses the more or less global font_expand_ratio. */
2023        result = tex_hpack(list, target, packing_substitute, hpack_dir, holding_none_option, box_limit_none);
2024    } else { 
2025        if (has_uleader) { 
2026           set_box_package_state(result, package_u_leader_found);
2027        }
2028        if (has_limit > 0) {
2029            tex_limit(result);
2030        }
2031    }
2032    /*tex Here we reset the |font_expand_ratio|. */
2033    lmt_packaging_state.font_expansion_ratio = 0;
2034    return result;
2035}
2036
2037halfword tex_filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, halfword d, int just_pack, halfword attr, int state, int retain)
2038{
2039    halfword head;
2040    singleword direction = (singleword) checked_direction_value(d);
2041    if (just_pack) {
2042        head = node_next(p);
2043    } else if (node_type(p) == temp_node && ! node_next(p)) {
2044        head = node_next(p);
2045    } else {
2046        /*tex Maybe here: |node_prev(p) = null|. */
2047        head = node_next(p);
2048        if (head) {
2049            node_prev(head) = null;
2050            if (tex_list_has_glyph(head)) {
2051                tex_handle_hyphenation(head, qt);
2052                head = tex_handle_glyphrun(head, grp, direction);
2053            }
2054            if (head) {
2055                /*tex ignores empty anyway. Maybe also pass tail? */
2056                head = lmt_hpack_filter_callback(head, w, m, grp, direction, attr);
2057            }
2058        }
2059    }
2060    head = tex_hpack(head, w, m, direction, retain, box_limit_none);
2061    if (has_box_package_state(head, package_u_leader_found)) {
2062        if (head && normalize_line_mode_option(flatten_h_leaders_mode)) { 
2063            if (! is_box_package_state(state, package_u_leader_delayed)) {
2064                tex_flatten_leaders(head, grp, just_pack, uleader_filtered_hpack, 0);
2065            }
2066        }
2067    }
2068    return head;
2069}
2070
2071/*tex Here is a function to calculate the natural whd of a (horizontal) node list. */
2072
2073scaledwhd tex_natural_hsizes(halfword p, halfword pp, glueratio g_mult, int g_sign, int g_order)
2074{
2075    scaledwhd siz = { .wd = 0, .ht = 0, .dp = 0, .ns = 0 };
2076    scaled gp = 0;
2077    scaled gm = 0;
2078    while (p && p != pp) {
2079        switch (node_type(p)) {
2080            case glyph_node:
2081                {
2082                    scaledwhd whd = tex_glyph_dimensions_ex(p);
2083                    siz.wd += whd.wd;
2084                    if (whd.ht > siz.ht) {
2085                        siz.ht = whd.ht;
2086                    }
2087                    if (whd.dp > siz.dp) {
2088                        siz.dp = whd.dp;
2089                    }
2090                    break;
2091                }
2092            case hlist_node:
2093            case vlist_node:
2094                {
2095                    scaled s = box_shift_amount(p);
2096                    scaledwhd whd = tex_pack_dimensions(p);
2097                    siz.wd += whd.wd;
2098                    if (whd.ht - s > siz.ht) {
2099                        siz.ht = whd.ht - s;
2100                    }
2101                    if (whd.dp + s > siz.dp) {
2102                        siz.dp = whd.dp + s;
2103                    }
2104                    break;
2105                }
2106            case unset_node:
2107                siz.wd += box_width(p);
2108                if (box_height(p) > siz.ht) {
2109                    siz.ht = box_height(p);
2110                }
2111                if (box_depth(p) > siz.dp) {
2112                    siz.dp = box_depth(p);
2113                }
2114                break;
2115            case rule_node:
2116                siz.wd += rule_width(p);
2117                if (rule_height(p) > siz.ht) {
2118                    siz.ht = rule_height(p);
2119                }
2120                if (rule_depth(p) > siz.dp) {
2121                    siz.dp = rule_depth(p);
2122                }
2123                break;
2124            case glue_node:
2125                siz.wd += glue_amount(p);
2126                switch (g_sign) {
2127                    case stretching_glue_sign:
2128                        if (glue_stretch_order(p) == g_order) {
2129                            gp += glue_stretch(p);
2130                        }
2131                        break;
2132                    case shrinking_glue_sign:
2133                        if (glue_shrink_order(p) == g_order) {
2134                            gm += glue_shrink(p);
2135                        }
2136                        break;
2137                }
2138                if (is_leader(p)) {
2139                    halfword gl = glue_leader_ptr(p);
2140                    halfword ht = 0;
2141                    halfword dp = 0;
2142                    switch (node_type(gl)) {
2143                        case hlist_node:
2144                        case vlist_node:
2145                            ht = box_height(gl);
2146                            dp = box_depth(gl);
2147                            break;
2148                        case rule_node:
2149                            ht = rule_height(gl);
2150                            dp = rule_depth(gl);
2151                            break;
2152                        /* how about glyph_node */
2153                    }
2154                    if (ht) {
2155                        siz.ht = ht;
2156                    }
2157                    if (dp > siz.dp) {
2158                        siz.dp = dp;
2159                    }
2160                }
2161                break;
2162            case kern_node:
2163                siz.wd += tex_kern_dimension_ex(p);
2164                break;
2165            case disc_node:
2166                {
2167                    scaledwhd whd = tex_natural_hsizes(disc_no_break_head(p), null, g_mult, g_sign, g_order); /* hm, really glue here?  */
2168                    siz.wd += whd.wd;
2169                    if (whd.ht > siz.ht) {
2170                        siz.ht = whd.ht;
2171                    }
2172                    if (whd.dp > siz.dp) {
2173                        siz.dp = whd.dp;
2174                    }
2175                }
2176                break;
2177            case math_node:
2178                if (tex_math_glue_is_zero(p) || tex_ignore_math_skip(p)) {
2179                    siz.wd += math_surround(p);
2180                } else {
2181                    siz.wd += math_amount(p);
2182                    switch (g_sign) {
2183                        case stretching_glue_sign:
2184                            if (math_stretch_order(p) == g_order) {
2185                                gp += math_stretch(p);
2186                            }
2187                            break;
2188                        case shrinking_glue_sign:
2189                            if (math_shrink_order(p) == g_order) {
2190                                gm += math_shrink(p);
2191                            }
2192                            break;
2193                    }
2194                }
2195                break;
2196            case sub_box_node:
2197                /* really? */
2198                break;
2199            case sub_mlist_node:
2200                {
2201                    /* hack */
2202                    scaledwhd whd = tex_natural_hsizes(kernel_math_list(p), null, normal_glue_multiplier, normal_glue_sign, normal_glue_sign);
2203                    siz.wd += whd.wd;
2204                    if (whd.ht > siz.ht) {
2205                        siz.ht = whd.ht;
2206                    }
2207                    if (whd.dp > siz.dp) {
2208                        siz.dp = whd.dp;
2209                    }
2210                }
2211                break;
2212            default:
2213                break;
2214        }
2215        p = node_next(p);
2216    }
2217    siz.ns = siz.wd;
2218    switch (g_sign) {
2219        case stretching_glue_sign:
2220            siz.wd += glueround((glueratio)(g_mult) * (glueratio)(gp));
2221            break;
2222        case shrinking_glue_sign:
2223            siz.wd -= glueround((glueratio)(g_mult) * (glueratio)(gm));
2224            break;
2225    }
2226    return siz;
2227}
2228
2229scaledwhd tex_natural_msizes(halfword p, int ignoreprime)
2230{
2231    scaledwhd siz = { .wd = 0, .ht = 0, .dp = 0, .ns = 0 };
2232    while (p) {
2233        switch (node_type(p)) {
2234            case glyph_node:
2235                {
2236                    scaledwhd whd = tex_glyph_dimensions_ex(p);
2237                    siz.wd += whd.wd;
2238                    if (whd.ht > siz.ht) {
2239                        siz.ht = whd.ht;
2240                    }
2241                    if (whd.dp > siz.dp) {
2242                        siz.dp = whd.dp;
2243                    }
2244                    break;
2245                }
2246            case hlist_node:
2247            case vlist_node:
2248                {
2249                    scaled s = box_shift_amount(p);
2250                    scaledwhd whd = ignoreprime ? tex_natural_msizes(box_list(p), ignoreprime) : tex_pack_dimensions(p);
2251                    siz.wd += whd.wd;
2252                    if (ignoreprime) {
2253                        switch (node_subtype(p)) { 
2254                            case math_prime_list:
2255                            case math_sup_list:
2256                            case math_scripts_list:
2257                                goto HERE;
2258                        }
2259                    }
2260                    if (whd.ht - s > siz.ht) {
2261                        siz.ht = whd.ht - s;
2262                    }
2263                  HERE:
2264                    if (whd.dp + s > siz.dp) {
2265                        siz.dp = whd.dp + s;
2266                    }
2267                    break;
2268                }
2269            case unset_node:
2270                siz.wd += box_width(p);
2271                if (box_height(p) > siz.ht) {
2272                    siz.ht = box_height(p);
2273                }
2274                if (box_depth(p) > siz.dp) {
2275                    siz.dp = box_depth(p);
2276                }
2277                break;
2278            case rule_node:
2279                siz.wd += rule_width(p);
2280                if (rule_height(p) > siz.ht) {
2281                    siz.ht = rule_height(p);
2282                }
2283                if (rule_depth(p) > siz.dp) {
2284                    siz.dp = rule_depth(p);
2285                }
2286                break;
2287            case glue_node:
2288                siz.wd += glue_amount(p);
2289                if (is_leader(p)) {
2290                    halfword gl = glue_leader_ptr(p);
2291                    halfword ht = 0;
2292                    halfword dp = 0;
2293                    switch (node_type(gl)) {
2294                        case hlist_node:
2295                        case vlist_node:
2296                            ht = box_height(gl);
2297                            dp = box_depth(gl);
2298                            break;
2299                        case rule_node:
2300                            ht = rule_height(gl);
2301                            dp = rule_depth(gl);
2302                            break;
2303                        /* how about glyph_node */
2304                    }
2305                    if (ht) {
2306                        siz.ht = ht;
2307                    }
2308                    if (dp > siz.dp) {
2309                        siz.dp = dp;
2310                    }
2311                }
2312                break;
2313            case kern_node:
2314                siz.wd += tex_kern_dimension_ex(p);
2315                break;
2316            case disc_node:
2317                {
2318                    scaledwhd whd = tex_natural_msizes(disc_no_break_head(p), ignoreprime);
2319                    siz.wd += whd.wd;
2320                    if (whd.ht > siz.ht) {
2321                        siz.ht = whd.ht;
2322                    }
2323                    if (whd.dp > siz.dp) {
2324                        siz.dp = whd.dp;
2325                    }
2326                }
2327                break;
2328            case math_node:
2329                if (tex_math_glue_is_zero(p) || tex_ignore_math_skip(p)) {
2330                    siz.wd += math_surround(p);
2331                } else {
2332                    siz.wd += math_amount(p);
2333                }
2334                break;
2335            case sub_box_node:
2336                break;
2337            case sub_mlist_node:
2338                {
2339                    scaledwhd whd = tex_natural_msizes(kernel_math_list(p), ignoreprime);
2340                    siz.wd += whd.wd;
2341                    if (whd.ht > siz.ht) {
2342                        siz.ht = whd.ht;
2343                    }
2344                    if (whd.dp > siz.dp) {
2345                        siz.dp = whd.dp;
2346                    }
2347                }
2348                break;
2349            default:
2350                break;
2351        }
2352        p = node_next(p);
2353    }
2354    siz.ns = siz.wd;
2355    return siz;
2356}
2357
2358scaledwhd tex_natural_vsizes(halfword p, halfword pp, glueratio g_mult, int g_sign, int g_order, int inserts)
2359{
2360    scaledwhd siz = { .wd = 0, .ht = 0, .dp = 0, .ns = 0 };
2361    scaled gp = 0;
2362    scaled gm = 0;
2363    halfword f = p; 
2364    int insertfound = 0;
2365    while (p && p != pp) {
2366        switch (node_type(p)) {
2367            case hlist_node:
2368            case vlist_node:
2369                {
2370                    scaled shift = box_shift_amount(p);
2371                    scaledwhd whd = tex_pack_dimensions(p);
2372                    if (whd.wd + shift > siz.wd) {
2373                        siz.wd = whd.wd + shift;
2374                    }
2375                    siz.ht += siz.dp + whd.ht;
2376                    siz.dp = whd.dp;
2377                }
2378                break;
2379            case unset_node:
2380                siz.ht += siz.dp + box_height(p);
2381                siz.dp = box_depth(p);
2382                if (box_width(p) > siz.wd) {
2383                    siz.wd = box_width(p);
2384                }
2385                break;
2386            case rule_node:
2387                siz.ht += siz.dp + rule_height(p);
2388                siz.dp = rule_depth(p);
2389                if (rule_width(p) > siz.wd) {
2390                    siz.wd = rule_width(p);
2391                }
2392                break;
2393            case glue_node:
2394                {
2395                    siz.ht += siz.dp + glue_amount(p);
2396                    siz.dp = 0;
2397                    if (is_leader(p)) {
2398                        halfword gl = glue_leader_ptr(p);
2399                        halfword wd = 0;
2400                        switch (node_type(gl)) {
2401                            case hlist_node:
2402                            case vlist_node:
2403                                wd = box_width(gl);
2404                                break;
2405                            case rule_node:
2406                                wd = rule_width(gl);
2407                                break;
2408                        }
2409                        if (wd > siz.wd) {
2410                            siz.wd = wd;
2411                        }
2412                    }
2413                    switch (g_sign) {
2414                        case stretching_glue_sign:
2415                            if (glue_stretch_order(p) == g_order) {
2416                                gp += glue_stretch(p);
2417                            }
2418                            break;
2419                        case shrinking_glue_sign:
2420                            if (glue_shrink_order(p) == g_order) {
2421                                gm += glue_shrink(p);
2422                            }
2423                            break;
2424                    }
2425                    break;
2426                }
2427            case kern_node:
2428                siz.ht += siz.dp + kern_amount(p);
2429                siz.dp = 0;
2430                break;
2431            case insert_node: 
2432                if (inserts) {
2433                    halfword height = tex_insert_height(p);
2434                    if (height > 0) {
2435                        siz.ht += siz.dp + height;
2436                        siz.dp = 0;
2437                        insertfound = 1;
2438                    }
2439                }
2440                break;
2441            case glyph_node:
2442                tex_confusion("glyph in vpack");
2443                break;
2444            case disc_node:
2445                tex_confusion("discretionary in vpack");
2446                break;
2447            default:
2448                break;
2449        }
2450        p = node_next(p);
2451    }
2452    if (insertfound) { 
2453        siz.ht += tex_insert_distances(f, pp ? node_next(pp) : null, NULL, NULL);
2454    }
2455    siz.ns = siz.ht;
2456    switch (g_sign) {
2457        case stretching_glue_sign:
2458            siz.ht += glueround((glueratio)(g_mult) * (glueratio)(gp));
2459            break;
2460        case shrinking_glue_sign:
2461            siz.ht -= glueround((glueratio)(g_mult) * (glueratio)(gm));
2462            break;
2463    }
2464    return siz;
2465}
2466
2467/*tex simplified variant with less memory access */
2468
2469halfword tex_natural_width(halfword p, halfword pp, glueratio g_mult, int g_sign, int g_order)
2470{
2471    scaled wd = 0;
2472    scaled gp = 0;
2473    scaled gm = 0;
2474    while (p && p != pp) {
2475        /* no real gain over check in switch */
2476        switch (node_type(p)) {
2477            case glyph_node:
2478                wd += tex_glyph_width(p); /* Plus expansion? */
2479                break;
2480            case hlist_node:
2481            case vlist_node:
2482            case unset_node:
2483                wd += box_width(p);
2484                break;
2485            case rule_node:
2486                wd += rule_width(p);
2487                break;
2488            case glue_node:
2489                wd += glue_amount(p);
2490                switch (g_sign) {
2491                    case stretching_glue_sign:
2492                        if (glue_stretch_order(p) == g_order) {
2493                            gp += glue_stretch(p);
2494                        }
2495                        break;
2496                    case shrinking_glue_sign:
2497                        if (glue_shrink_order(p) == g_order) {
2498                            gm += glue_shrink(p);
2499                        }
2500                        break;
2501                }
2502                break;
2503            case kern_node:
2504                wd += kern_amount(p); // + kern_expansion(p);
2505                break;
2506            case disc_node:
2507                wd += tex_natural_width(disc_no_break(p), null, g_mult, g_sign, g_order);
2508                break;
2509            case math_node:
2510                if (tex_math_glue_is_zero(p) || tex_ignore_math_skip(p)) {
2511                    wd += math_surround(p);
2512                } else {
2513                    wd += math_amount(p);
2514                    switch (g_sign) {
2515                        case stretching_glue_sign:
2516                            if (math_stretch_order(p) == g_order) {
2517                                gp += math_stretch(p);
2518                            }
2519                            break;
2520                        case shrinking_glue_sign:
2521                            if (math_shrink_order(p) == g_order) {
2522                                gm += math_shrink(p);
2523                            }
2524                            break;
2525                    }
2526                }
2527                break;
2528            default:
2529                break;
2530        }
2531        p = node_next(p);
2532    }
2533    switch (g_sign) {
2534        case stretching_glue_sign:
2535            wd += glueround((glueratio) (g_mult) * (glueratio) (gp));
2536            break;
2537        case shrinking_glue_sign:
2538            wd -= glueround((glueratio) (g_mult) * (glueratio) (gm));
2539            break;
2540    }
2541    return wd;
2542}
2543
2544halfword tex_natural_hsize(halfword p, halfword *correction)
2545{
2546    scaled wd = 0;
2547    halfword c = null;
2548    while (p) {
2549        switch (node_type(p)) {
2550            case glyph_node:
2551                wd += tex_glyph_width(p); /* Plus expansion? */
2552                break;
2553            case hlist_node:
2554            case vlist_node:
2555            case unset_node:
2556                wd += box_width(p);
2557                break;
2558            case rule_node:
2559                wd += rule_width(p);
2560                break;
2561            case glue_node:
2562                wd += glue_amount(p);
2563                if (node_subtype(p) == correction_skip_glue) {
2564                    c = p;
2565                }
2566                break;
2567            case kern_node:
2568                wd += kern_amount(p); //  + kern_expansion(p);
2569                break;
2570            case disc_node:
2571                wd += tex_natural_hsize(disc_no_break(p), NULL);
2572                break;
2573            case math_node:
2574                if (tex_math_glue_is_zero(p) || tex_ignore_math_skip(p)) {
2575                    wd += math_surround(p);
2576                } else {
2577                    wd += math_amount(p);
2578                }
2579                break;
2580            default:
2581                break;
2582        }
2583        p = node_next(p);
2584    }
2585    if (correction) {
2586        *correction = c;
2587    }
2588    return wd;
2589}
2590
2591halfword tex_natural_vsize(halfword p)
2592{
2593    scaled ht = 0;
2594    scaled dp = 0;
2595    while (p) {
2596        switch (node_type(p)) {
2597            case hlist_node:
2598            case vlist_node:
2599                ht += dp + box_height(p);
2600                dp = box_depth(p);
2601                break;
2602            case unset_node:
2603                ht += dp + box_height(p);
2604                dp = box_depth(p);
2605                break;
2606            case rule_node:
2607                ht += dp + rule_height(p);
2608                dp = rule_depth(p);
2609                break;
2610            case glue_node:
2611                ht += dp + glue_amount(p);
2612                dp = 0;
2613                break;
2614            case kern_node:
2615                ht += dp + kern_amount(p);
2616                dp = 0;
2617                break;
2618            default:
2619                break;
2620        }
2621        p = node_next(p);
2622    }
2623    return ht + dp;
2624}
2625
2626/*tex
2627
2628    The |vpack| subroutine is actually a special case of a slightly more general routine called
2629    |vpackage|, which has four parameters. The fourth parameter, which is |max_dimension| in the case
2630    of |vpack|, specifies the maximum depth of the page box that is constructed. The depth is first
2631    computed by the normal rules; if it exceeds this limit, the reference point is simply moved
2632    down until the limiting depth is attained. We actually hav efive parameters because we also
2633    deal with teh direction.
2634
2635*/
2636
2637halfword tex_vpack(halfword p, scaled targetheight, int m, scaled targetdepth, singleword pack_direction, int retain, scaled *excess)
2638{
2639    scaled width = 0;
2640    scaled depth = 0;
2641    scaled height = 0;
2642    int has_uleader = 0;
2643    /*tex the box node that will be returned */
2644    halfword box = tex_new_node(vlist_node, unknown_list);
2645    int has_limit = box_limit_mode_vlist ? 1 : -1;
2646    (void) retain; /* todo */
2647    box_dir(box) = pack_direction;
2648    box_shift_amount(box) = 0;
2649    box_list(box) = p;
2650    lmt_packaging_state.last_badness = 0;
2651    lmt_packaging_state.last_overshoot = 0;
2652    for (int i = normal_glue_order; i <= filll_glue_order; i++) {
2653        lmt_packaging_state.total_stretch[i] = 0;
2654        lmt_packaging_state.total_shrink[i] = 0;
2655    }
2656    while (p) {
2657        /*tex
2658
2659            Examine node |p| in the vlist, taking account of its effect on the dimensions of the
2660            new box; then advance |p| to the next node.
2661
2662        */
2663        halfword n = node_next(p);
2664        switch (node_type(p)) {
2665            case hlist_node:
2666            case vlist_node:
2667                {
2668                    /*tex
2669
2670                        Incorporate box dimensions into the dimensions of the vbox that will
2671                        contain it.
2672
2673                    */
2674                    scaled shift = box_shift_amount(p);
2675                    scaledwhd whd = tex_pack_dimensions(p);
2676                    if (whd.wd + shift > width) {
2677                        width = whd.wd + shift;
2678                    }
2679                    height += depth + whd.ht;
2680                    depth = whd.dp;
2681                    tex_aux_promote_pre_migrated(box, p);
2682                    tex_aux_promote_post_migrated(box, p);
2683                }
2684                break;
2685            case unset_node:
2686                height += depth + box_height(p);
2687                depth = box_depth(p);
2688                if (box_width(p) > width) {
2689                    width = box_width(p);
2690                }
2691                /* No promotions here. */
2692                break;
2693            case rule_node:
2694                height += depth + rule_height(p);
2695                depth = rule_depth(p);
2696                if (rule_width(p) > width) {
2697                    width = rule_width(p);
2698                }
2699                break;
2700            case glue_node:
2701                /*tex Incorporate glue into the vertical totals. */
2702                height += depth + glue_amount(p);
2703                depth = 0;
2704                lmt_packaging_state.total_stretch[glue_stretch_order(p)] += glue_stretch(p);
2705                lmt_packaging_state.total_shrink[glue_shrink_order(p)] += glue_shrink(p);
2706                if (is_leader(p)) {
2707                    halfword gl = glue_leader_ptr(p);
2708                    scaled wd = 0;
2709                    switch (node_type(gl)) {
2710                        case hlist_node:
2711                        case vlist_node:
2712                            wd = box_width(gl);
2713                            break;
2714                        case rule_node:
2715                            wd = rule_width(gl);
2716                            break;
2717                    }
2718                    if (wd > width) {
2719                        width = wd;
2720                    }
2721                    if (node_subtype(p) == u_leaders) {
2722                        has_uleader = 1;
2723                    }
2724                }
2725                if (! has_limit && tex_has_glue_option(p, glue_option_limit)) {
2726                    has_limit = 1;
2727                }
2728                break;
2729            case kern_node:
2730                height += depth + kern_amount(p);
2731                depth = 0;
2732                break;
2733            case insert_node:
2734                if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_insert)) {
2735                    halfword l = insert_list(p);
2736                    tex_aux_post_migrate(box, p);
2737                    while (l) {
2738                        l = node_type(l) == insert_node ? tex_aux_post_migrate(box, l) : node_next(l);
2739                    }
2740                }
2741                break;
2742            case mark_node:
2743                 if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_mark)) {
2744                    tex_aux_post_migrate(box, p);
2745                 }
2746                 break;
2747            case glyph_node:
2748                tex_confusion("glyph in vpack");
2749                break;
2750            case disc_node:
2751                tex_confusion("discretionary in vpack");
2752                break;
2753            default:
2754                break;
2755        }
2756        p = n;
2757    }
2758    box_width(box) = width;
2759    if (depth > targetdepth) {
2760        height += depth - targetdepth;
2761        box_depth(box) = targetdepth;
2762    } else {
2763        box_depth(box) = depth;
2764    }
2765    /*tex
2766
2767        Determine the value of |height(r)| and the appropriate glue setting; then |return| or |goto
2768        common_ending|. When we get to the present part of the program, |x| is the natural height of
2769        the box being packaged.
2770    */
2771    if (m == packing_additional) {
2772        targetheight += height;
2773    }
2774    box_height(box) = targetheight;
2775    {
2776        scaled targetexcess = targetheight - height;
2777        /*tex Now |x| is the excess to be made up. */
2778        if (excess) { 
2779            *excess = targetexcess; 
2780        }
2781        if (targetexcess == 0) {
2782            box_glue_sign(box) = normal_glue_sign;
2783            box_glue_order(box) = normal_glue_order;
2784            box_glue_set(box) = 0.0;
2785         // goto EXIT;
2786        } else if (targetexcess > 0) {
2787            /*tex Determine vertical glue stretch setting, then |return| or |goto common_ending|. */
2788            halfword order = tex_aux_used_order(lmt_packaging_state.total_stretch);
2789            box_glue_order(box) = order;
2790            box_glue_sign(box) = stretching_glue_sign;
2791            if (lmt_packaging_state.total_stretch[order] != 0) {
2792                box_glue_set(box) = (glueratio) ((double) targetexcess / lmt_packaging_state.total_stretch[order]);
2793            } else {
2794                /*tex There's nothing to stretch. */
2795                box_glue_sign(box) = normal_glue_sign;
2796                box_glue_set(box) = 0.0;
2797            }
2798            if (order == normal_glue_order && box_list(box)) {
2799                /*tex Report an underfull vbox and |goto common_ending|, if this box is sufficiently bad. */
2800                lmt_packaging_state.last_badness = tex_badness(targetexcess, lmt_packaging_state.total_stretch[normal_glue_order]);
2801                if (lmt_packaging_state.last_badness > vbadness_par) {
2802                    int what = badness_mode_nothing;
2803                    if (vbadness_mode_par & badness_mode_loose) { 
2804                        what = badness_mode_loose;
2805                    } else if ((vbadness_mode_par & badness_mode_underfull) && (lmt_packaging_state.last_badness > 100)) {
2806                        what = badness_mode_underfull;
2807                    }
2808                    if (what != badness_mode_nothing) {
2809                        int callback_id = lmt_callback_defined(vpack_quality_callback);
2810                        const char *verdict = what == badness_mode_underfull ? "underfull" : "loose"; // beware, this needs to be adapted to the configureable 99 
2811                        if (callback_id > 0) {
2812                            lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "SdNddS->",
2813                                verdict,
2814                                lmt_packaging_state.last_badness,
2815                                box,
2816                                abs(lmt_packaging_state.pack_begin_line),
2817                                lmt_input_state.input_line,
2818                                tex_current_input_file_name()
2819                            );
2820                         // goto EXIT;
2821                        } else {
2822                            tex_print_nlp();
2823                            tex_print_format("%l[package: %s \\vbox (badness %i)", verdict, lmt_packaging_state.last_badness);
2824                            goto COMMON_ENDING;
2825                        }
2826                    }
2827                }
2828            }
2829         // goto EXIT;
2830        } else {
2831            /*tex Determine vertical glue shrink setting, then |return| or |goto common_ending|. */
2832            halfword order = tex_aux_used_order(lmt_packaging_state.total_shrink);
2833            box_glue_order(box) = order;
2834            box_glue_sign(box) = shrinking_glue_sign;
2835            if (lmt_packaging_state.total_shrink[order] != 0) {
2836                box_glue_set(box) = (glueratio) ((double) (-targetexcess) / lmt_packaging_state.total_shrink[order]);
2837            } else {
2838                /*tex There's nothing to shrink. */
2839                box_glue_sign(box) = normal_glue_sign;
2840                box_glue_set(box) = 0.0;
2841            }
2842            if (order == normal_glue_order && box_list(box)) {
2843                if (lmt_packaging_state.total_shrink[order] < -targetexcess) {
2844                    int overshoot = -targetexcess - lmt_packaging_state.total_shrink[normal_glue_order];
2845                    lmt_packaging_state.last_badness = scaling_factor_squared;
2846                    lmt_packaging_state.last_overshoot = overshoot;
2847                    /*tex Use the maximum shrinkage */
2848                    box_glue_set(box)  = 1.0;
2849                    /*tex Report an overfull vbox and |goto common_ending|, if this box is sufficiently bad. */
2850                    if (((overshoot > vfuzz_par) || (vbadness_par < 100)) && (vbadness_mode_par & badness_mode_overfull)) {
2851                        int callback_id = lmt_callback_defined(vpack_quality_callback);
2852                        if (callback_id > 0) {
2853                            lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "SdNddS->",
2854                                "overfull",
2855                                overshoot,
2856                                box,
2857                                abs(lmt_packaging_state.pack_begin_line),
2858                                lmt_input_state.input_line,
2859                                tex_current_input_file_name()
2860                            );
2861                         // goto EXIT;
2862                        } else {
2863                            tex_print_nlp();
2864                            tex_print_format("%l[package: overfull \\vbox (%p too high)", overshoot);
2865                            goto COMMON_ENDING;
2866                        }
2867                    }
2868                } else {
2869                    /*tex Report a tight vbox and |goto common_ending|, if this box is sufficiently bad. */
2870                    lmt_packaging_state.last_badness = tex_badness(-targetexcess, lmt_packaging_state.total_shrink[normal_glue_order]);
2871                    if (lmt_packaging_state.last_badness > vbadness_par && (vbadness_mode_par & badness_mode_tight)) {
2872                        int callback_id = lmt_callback_defined(vpack_quality_callback);
2873                        if (callback_id > 0) {
2874                            lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "SdNddS->",
2875                                "tight",
2876                                lmt_packaging_state.last_badness,
2877                                box,
2878                                abs(lmt_packaging_state.pack_begin_line),
2879                                lmt_input_state.input_line,
2880                                tex_current_input_file_name()
2881                            );
2882                         // goto EXIT;
2883                        } else {
2884                            tex_print_nlp();
2885                            tex_print_format("%l[package: tight \\vbox (badness %i)", lmt_packaging_state.last_badness);
2886                            goto COMMON_ENDING;
2887                        }
2888                    }
2889                }
2890            }
2891         // goto EXIT;
2892        }
2893    }
2894    goto EXIT;
2895  COMMON_ENDING:
2896    /*tex Finish issuing a diagnostic message or an overfull or underfull vbox. */
2897    if (lmt_page_builder_state.output_active) {
2898        tex_print_format(" has occurred while \\output is active]");
2899    } else if (lmt_packaging_state.pack_begin_line != 0) {
2900        tex_print_format(" in alignment at lines %i--%i]", abs(lmt_packaging_state.pack_begin_line), lmt_input_state.input_line);
2901    } else {
2902        tex_print_format(" detected at line %i]", lmt_input_state.input_line);
2903    }
2904    tex_print_ln();
2905    tex_begin_diagnostic();
2906    tex_show_box(box);
2907    tex_end_diagnostic();
2908  EXIT:
2909    if (has_uleader) { 
2910        set_box_package_state(box, package_u_leader_found);
2911    }
2912    if (has_limit > 0) {
2913        tex_limit(box);
2914    }
2915    /*tex Further (experimental) actions can go here. */
2916    return box;
2917}
2918
2919halfword tex_filtered_vpack(halfword p, scaled h, int m, scaled maxdepth, int grp, halfword direction, int just_pack, halfword attr, int state, int retain, int *excess)
2920{
2921    halfword result = p;
2922    if (! just_pack) {
2923        /* We have to do it here because we cannot nest packaging! */
2924        if (result && normalize_line_mode_option(flatten_h_leaders_mode)) { 
2925            halfword c = result; 
2926            while (c) {
2927                switch (node_type(c)) {
2928                    case hlist_node:
2929                    case vlist_node:
2930                        if (has_box_package_state(c, package_u_leader_found) && ! is_box_package_state(state, package_u_leader_delayed)) {
2931                            tex_flatten_leaders(c, grp, just_pack, uleader_before_vpack, 0);
2932                        }
2933                        break;
2934                }
2935                c = node_next(c);
2936            }
2937        }
2938        result = lmt_vpack_filter_callback(result, h, m, maxdepth, grp, direction, attr);
2939    }
2940    result = tex_vpack(result, h, m, maxdepth, (singleword) checked_direction_value(direction), retain, excess);
2941    if (result && normalize_par_mode_option(flatten_v_leaders_mode) && ! is_box_package_state(state, package_u_leader_delayed)) {
2942        tex_flatten_leaders(result, grp, just_pack, uleader_after_vpack, 0);
2943    }
2944    if (! just_pack) {
2945        result = lmt_packed_vbox_filter_callback(result, grp);
2946    }
2947    return result;
2948}
2949
2950static scaled tex_aux_first_height(halfword boxnode) 
2951{
2952    halfword list = box_list(boxnode);
2953    if (list) {
2954        switch (node_type(list)) {
2955            case hlist_node:
2956            case vlist_node:
2957                return box_height(list);
2958            case rule_node:
2959                return rule_height(list);
2960        }
2961    }
2962    return 0;
2963}
2964
2965static void tex_aux_set_vnature(halfword boxnode, int nature)
2966{
2967    switch (nature) { 
2968        case vtop_code: 
2969        case tsplit_code: 
2970        case vbalanced_top_code: 
2971            {
2972                /*tex
2973
2974                    Read just the height and depth of |boxnode| (|boxnode|), for |\vtop|. The height of
2975                    a |\vtop| box is inherited from the first item on its list, if that item is an
2976                    |hlist_node|, |vlist_node|, or |rule_node|; otherwise the |\vtop| height is zero.
2977
2978                */
2979                scaled height = tex_aux_first_height(boxnode);
2980                box_depth(boxnode) = box_total(boxnode) - height;
2981                box_height(boxnode) = height;
2982                box_package_state(boxnode) = vtop_package_state;
2983            }
2984            break;
2985        case vbox_code: 
2986        case vsplit_code: 
2987        case vbalance_code: 
2988        case vbalanced_box_code: 
2989     // case vbalanced_insert_code: 
2990     // case vbalanced_discard_code: 
2991     // case vbalanced_unsert_code: 
2992     // case vbalanced_reinsert_code:
2993        case flush_mvl_box_code: 
2994            box_package_state(boxnode) = vbox_package_state;
2995            break;
2996        case dbox_code: 
2997        case dsplit_code: 
2998            box_package_state(boxnode) = dbox_package_state;
2999            break;
3000    }
3001}
3002
3003
3004/*tex
3005    Here we always start out in l2r mode and without shift. After all we need to be compatible with
3006    how it was before.
3007*/
3008
3009void tex_run_vcenter(void)
3010{
3011    tex_aux_scan_full_spec(direct_box_flag, vcenter_group, direction_l2r, 0, 0, -1, 0, 0);
3012    tex_normal_paragraph(vcenter_par_context);
3013    tex_push_nest();
3014    cur_list.mode = internal_vmode;
3015    cur_list.prev_depth = ignore_depth_criterion_par;
3016    if (every_vbox_par) {
3017        tex_begin_token_list(every_vbox_par, every_vbox_text);
3018    }
3019}
3020
3021void tex_finish_vcenter_group(void)
3022{
3023    if (! tex_wrapped_up_paragraph(vcenter_par_context, 1)) {
3024        halfword p;
3025        tex_end_paragraph(vcenter_group, vcenter_par_context);
3026        tex_package(vbox_code); /* todo: vcenter_code */
3027        p = tex_pop_tail();
3028        if (p) {
3029            switch (node_type(p)) {
3030                case vlist_node:
3031                    {
3032                        scaled delta = box_total(p);
3033                        box_height(p) = tex_half_scaled(delta);
3034                        box_depth(p) = delta - box_height(p);
3035                        break;
3036                    }
3037                case simple_noad:
3038                    node_subtype(p) = vcenter_noad_subtype;
3039                    break;
3040                /*
3041                case style_node:
3042                    break;
3043                */
3044            }
3045            tex_tail_append(p);
3046        }
3047    }
3048}
3049
3050void tex_package(singleword nature)
3051{
3052    int grp = cur_group;
3053    scaled maxdepth = box_max_depth_par;
3054    tex_unsave();
3055    lmt_save_state.save_stack_data.ptr -= saved_box_n_of_records;
3056    {
3057        halfword boxnode = null; /*tex Aka |cur_box|. */
3058        /*tex 
3059            We need to save some over the callback because it can do something that messes with 
3060            the stack entries that has already been decremented. 
3061        */
3062        halfword slot = saved_box_slot;
3063        halfword context = saved_box_context;
3064        halfword dirptr = saved_box_dir_pointer;
3065        halfword orientation = saved_box_orientation;
3066        halfword anchor = saved_box_anchor;
3067        halfword geometry = saved_box_geometry;
3068        halfword attrlist = saved_box_attr_list;
3069        halfword state = saved_box_state;
3070        scaled shift = saved_box_shift;
3071        halfword source = saved_box_source;
3072        halfword target = saved_box_target;
3073        halfword axis = saved_box_axis;
3074        halfword mainclass = saved_box_class;
3075        halfword callback = saved_box_callback;
3076        halfword leaders = saved_box_leaders;
3077        halfword options = saved_box_options;
3078        scaled xoffset = saved_box_xoffset;
3079        scaled yoffset = saved_box_yoffset;
3080        scaled xmove = saved_box_xmove;
3081        scaled ymove = saved_box_ymove;
3082        if (cur_list.mode == restricted_hmode) {
3083            boxnode = tex_filtered_hpack(
3084                cur_list.head, cur_list.tail, 
3085                saved_box_amount, saved_box_packing, grp, 
3086                saved_box_direction, saved_box_pack, attrlist, state, saved_box_retain
3087            );
3088            node_subtype(boxnode) = hbox_list;
3089            if (options & saved_box_reverse_option) {
3090                box_list(boxnode) = tex_reversed_node_list(box_list(boxnode));
3091            }
3092            box_package_state(boxnode) = hbox_package_state;
3093        } else {
3094            boxnode = tex_filtered_vpack(
3095                node_next(cur_list.head), 
3096                saved_box_amount, saved_box_packing, maxdepth, grp, 
3097                saved_box_direction, saved_box_pack, attrlist, state, saved_box_retain, 
3098                NULL
3099            );
3100            box_package_state(boxnode) = hbox_package_state;
3101            tex_aux_set_vnature(boxnode, nature);
3102        }
3103        if (options & saved_box_limit_option && box_list(boxnode)) {
3104            tex_limit(boxnode);
3105        }
3106        if (dirptr) {
3107            /*tex Adjust back |text_dir_ptr| for |scan_spec| */
3108            tex_flush_node_list(lmt_dir_state.text_dir_ptr);
3109            lmt_dir_state.text_dir_ptr = dirptr;
3110        }
3111        /*
3112            An attribute is not assigned beforehand, just passed. But, when some is assigned we need 
3113            to retain it. So, how do we deal with attributes that are added? Maybe we have to merge
3114            changes? Or maybe an extra option in hpack ... some day.
3115        */
3116        tex_attach_attribute_list_attribute(boxnode, attrlist);
3117        delete_attribute_reference(attrlist);
3118        /* */
3119        if (tex_has_geometry(geometry, offset_geometry) || tex_has_geometry(geometry, orientation_geometry)) {
3120            scaled wd = box_width(boxnode);
3121            scaled ht = box_height(boxnode);
3122            scaled dp = box_depth(boxnode);
3123            if (xmove) {
3124                xoffset = tex_aux_checked_dimension1(xoffset + xmove);
3125                wd = tex_aux_checked_dimension2(wd + xmove);
3126            }
3127            if (ymove) {
3128                yoffset = tex_aux_checked_dimension1(yoffset + ymove);
3129                ht = tex_aux_checked_dimension2(ht + ymove);
3130                dp = tex_aux_checked_dimension2(dp - ymove);
3131            }
3132            box_w_offset(boxnode) = wd;
3133            box_h_offset(boxnode) = ht;
3134            box_d_offset(boxnode) = dp;
3135            switch (orientationonly(orientation)) {
3136                case 0 : /*   0 */
3137                    break;
3138                case 2 : /* 180 */
3139                    box_height(boxnode) = dp;
3140                    box_depth(boxnode) = ht;
3141                    geometry |= orientation_geometry;
3142                    break;
3143                case 1 : /*  90 */
3144                case 3 : /* 270 */
3145                    box_width(boxnode) = ht + dp;
3146                    box_height(boxnode) = wd;
3147                    box_depth(boxnode) = 0;
3148                    geometry |= orientation_geometry;
3149                    break;
3150                case 4 : /*   0 */
3151                    box_height(boxnode) = ht + dp;
3152                    box_depth(boxnode) = 0;
3153                    geometry |= orientation_geometry;
3154                    break;
3155                case 5 : /* 180 */
3156                    box_height(boxnode) = 0;
3157                    box_depth(boxnode) = ht + dp;
3158                    geometry |= orientation_geometry;
3159                    break;
3160                default :
3161                    break;
3162            }
3163            if (xoffset || yoffset) {
3164                box_x_offset(boxnode) = xoffset;
3165                box_y_offset(boxnode) = yoffset;
3166                geometry |= offset_geometry;
3167            }
3168        }
3169        if (source || target) {
3170            box_source_anchor(boxnode) = source;
3171            box_target_anchor(boxnode) = target;
3172            geometry |= anchor_geometry;
3173        }
3174        box_anchor(boxnode) = anchor;
3175        box_orientation(boxnode) = orientation;
3176        box_geometry(boxnode) = (singleword) geometry;
3177        if (options & saved_box_container_option) {
3178            node_subtype(boxnode) = container_list;
3179        } else if (options & saved_box_mathtext_option) {
3180            node_subtype(boxnode) = math_text_list;
3181        }
3182        if (axis) { 
3183            tex_add_box_option(boxnode, box_option_no_math_axis);
3184        } else { 
3185            tex_remove_box_option(boxnode, box_option_no_math_axis);
3186        }
3187if (options & saved_box_discardable_option) {
3188    box_options(boxnode) |= box_option_discardable;
3189}
3190        box_package_state(boxnode) |= (singleword) state;
3191        tex_pop_nest();
3192        tex_box_end(context, boxnode, shift, mainclass, slot, callback, leaders);
3193    }
3194}
3195
3196static int local_box_mapping[] = { 
3197    [local_left_box_box_code  ] = local_left_box_code,
3198    [local_right_box_box_code ] = local_right_box_code,
3199    [local_middle_box_box_code] = local_middle_box_code
3200};
3201
3202void tex_run_unpackage(void)
3203{
3204    int code = cur_chr; /*tex should we copy? */
3205    switch (code) {
3206        case box_code:
3207        case copy_code:
3208        case unpack_code:
3209            {
3210                halfword index = tex_scan_box_register_number();
3211                halfword box = box_register(index);
3212                if (box) {
3213                    int bad = 0;
3214                    switch (cur_list.mode) {
3215                        case vmode: 
3216                        case internal_vmode: 
3217                            if (node_type(box) != vlist_node) { 
3218                                bad = 1;
3219                            }
3220                            break;
3221                        case hmode: 
3222                        case restricted_hmode: 
3223                            if (node_type(box) != hlist_node) { 
3224                                bad = 1;
3225                            }
3226                            break;
3227                        case mmode: 
3228                        case inline_mmode: 
3229                            bad = 1;
3230                            break;
3231                    }
3232                    if (bad) {
3233                        tex_handle_error(
3234                            normal_error_type,
3235                            "Incompatible list can't be unboxed",
3236                            "Sorry, Pandora. (You sneaky devil.) I refuse to unbox an \\hbox in vertical mode\n"
3237                            "or vice versa. And I can't open any boxes in math mode."
3238                        );
3239                    } else {
3240                        /*tex 
3241                            We go via variables because we do varmem assignments. Probably this is 
3242                            not needed.
3243                        */
3244                        halfword tail = cur_list.tail;
3245                        halfword list = box_list(box);
3246                        halfword pre_migrated  = code == unpack_code ? null : box_pre_migrated(box);
3247                        halfword post_migrated = code == unpack_code ? null : box_post_migrated(box);
3248                        /* probably overkill as we inject vadjust again, maybe it should be an option, 'bind' or so */
3249                        halfword pre_adjusted  = code == unpack_code ? null : box_pre_adjusted(box);
3250                        halfword post_adjusted = code == unpack_code ? null : box_post_adjusted(box);
3251                        int prunekerns = list && node_type(box) == hlist_node && normalize_line_mode_option(remove_margin_kerns_mode);
3252                        /*tex 
3253                            This topskip move is an ugly catch for wrong usage: when messing with 
3254                            vboxes the migrated material can end up outside the unvboxed (pagebody)
3255                            content which is not what one wants. The way to prevent is to force 
3256                            hmode when unboxing. It makes no sense to complicate this mechanism 
3257                            even more by soem fragile hackery. Best just create what gets asked 
3258                            and remember that thsi was decided. We need to copy the list earlier 
3259                            when we do this! 
3260                        */
3261                        /*
3262                        if (list) { 
3263                            if (code == copy_code) {
3264                                list = tex_copy_node_list(list, null);
3265                            } else {
3266                                box_list(box) = null;
3267                            }     
3268                        }
3269                        {
3270                            int movetopskip = list && (pre_adjusted || pre_migrated) && node_type(list) == glue_node && node_subtype(list) == top_skip_glue;
3271                            if (movetopskip) {
3272                                halfword topskip = list; 
3273                                list = node_next(list);
3274                                node_prev(topskip) = null;
3275                                node_next(topskip) = null;
3276                                tex_try_couple_nodes(tail, topskip);
3277                                tail = topskip;
3278                                if (list) { 
3279                                    node_prev(list) = null;
3280                                }
3281                                if (pre_adjusted && tracing_adjusts_par > 1) {
3282                                    tex_begin_diagnostic();
3283                                    tex_print_format("[adjust: topskip moved]");
3284                                    tex_end_diagnostic();
3285                                }
3286                            }
3287                        }
3288                        */ 
3289                        if (pre_adjusted) {
3290                            if (code == copy_code) {
3291                                pre_adjusted  = tex_copy_node_list(pre_adjusted, null);
3292                            } else {
3293                                box_pre_adjusted(box) = null;
3294                            }
3295                            tail = tex_flush_adjust_prepend(pre_adjusted, tail);
3296                        }
3297                        if (pre_migrated) {
3298                            if (code == copy_code) {
3299                                pre_migrated  = tex_copy_node_list(pre_migrated, null);
3300                            } else {
3301                                box_pre_migrated(box) = null;
3302                            }
3303                            tex_try_couple_nodes(tail, pre_migrated);
3304                            tail = tex_tail_of_node_list(pre_migrated);
3305                        }
3306                        if (list) {
3307                            if (code == copy_code) {
3308                                list = tex_copy_node_list(list, null);
3309                            } else {
3310                                box_list(box) = null;
3311                            }     
3312                            tex_try_couple_nodes(tail, list);
3313                            tail = tex_tail_of_node_list(list);
3314                        }
3315                        if (post_migrated) {
3316                            if (code == copy_code) {
3317                                post_migrated = tex_copy_node_list(post_migrated, null);
3318                            } else {
3319                                box_post_migrated(box) = null;
3320                            }
3321                            tex_try_couple_nodes(tail, post_migrated);
3322                            tail = tex_tail_of_node_list(post_migrated);
3323                        }
3324                        if (post_adjusted) {
3325                            if (code == copy_code) {
3326                                post_adjusted = tex_copy_node_list(post_adjusted, null);
3327                            } else {
3328                                box_post_adjusted(box) = null;
3329                            }
3330                            tail = tex_flush_adjust_append(post_adjusted, tail);
3331                        }
3332                        if (code != copy_code) {
3333                            box_register(index) = null;
3334                            tex_flush_node(box);
3335                        }
3336                        cur_list.tail = prunekerns ? tex_wipe_margin_kerns(cur_list.head) : tex_tail_of_node_list(tail);
3337                    }
3338                }
3339                break;
3340            }
3341        case page_discards_code:
3342            {
3343                tex_try_couple_nodes(cur_list.tail, lmt_packaging_state.page_discards_head);
3344                cur_list.tail = tex_tail_of_node_list(cur_list.tail);
3345                lmt_packaging_state.page_discards_head = null;
3346                break;
3347            }
3348        case split_discards_code:
3349        case copy_split_discards_code:
3350            {
3351                halfword discards = lmt_packaging_state.split_discards_head;
3352                if (discards) {
3353                    if (code == copy_split_discards_code) { 
3354                        discards = tex_copy_node_list(discards, null);
3355                    }
3356                    tex_couple_nodes(cur_list.tail, discards);
3357                    cur_list.tail = tex_tail_of_node_list(cur_list.tail);
3358                    lmt_packaging_state.split_discards_head = null;
3359                }
3360                break;
3361            }
3362        case insert_box_code:
3363        case insert_copy_code:
3364            {
3365                /*
3366                    This one is sensitive for messing with callbacks. Somehow attributes and the if
3367                    stack ifs can get corrupted but I have no clue yet how that happens but temp
3368                    nodes have the same size so ...
3369                */
3370                halfword index = tex_scan_integer(0, NULL, NULL);
3371                if (tex_valid_insert_id(index)) {
3372                    halfword boxnode = tex_get_insert_content(index); /* also checks for id */
3373                    if (boxnode) {
3374                        if (! is_v_mode(cur_list.mode)) {
3375                            tex_handle_error(
3376                                normal_error_type,
3377                                "Unpacking an inserts can only happen in vertical mode.",
3378                                NULL
3379                            );
3380                        } else if (node_type(boxnode) == vlist_node) {
3381                            if (code == insert_copy_code) {
3382                                boxnode = tex_copy_node(boxnode);
3383                            } else {
3384                                tex_set_insert_content(index, null);
3385                            }
3386                            if (boxnode) {
3387                                halfword list = box_list(boxnode);
3388                                if (list) {
3389                                    tex_try_couple_nodes(cur_list.tail, list);
3390                                    cur_list.tail = tex_tail_of_node_list(list);
3391                                    box_list(boxnode) = null;
3392                                }
3393                                tex_flush_node(boxnode);
3394                            }
3395                        } else {
3396                            /* error, maybe migration list  */
3397                        }
3398                    }
3399                }
3400                break;
3401            }
3402        case local_left_box_box_code:
3403        case local_right_box_box_code:
3404        case local_middle_box_box_code:
3405            {
3406                tex_try_couple_nodes(cur_list.tail, tex_get_local_boxes(local_box_mapping[code]));
3407                cur_list.tail = tex_tail_of_node_list(cur_list.tail);
3408                break;
3409            }
3410        default:
3411            {
3412                tex_confusion("weird unpackage");
3413                break;
3414            }
3415    }
3416}
3417
3418/*tex
3419
3420    When a box is being appended to the current vertical list, the baselineskip calculation is
3421    handled by the |append_to_vlist| routine.
3422
3423    Todo: maybe store some more in lines, so that we can get more consistent spacing (for instance
3424    the |baseline_skip_par| and |prev_depth_par| are now pars and not values frozen with the line.
3425    But as usual we can expect side effects so \unknown
3426
3427*/
3428
3429static halfword tex_aux_depth_correction(halfword b, const line_break_properties *properties)
3430{
3431    /*tex The deficiency of space between baselines: */
3432    halfword p;
3433    halfword height = has_box_package_state(b, dbox_package_state) ? tex_aux_first_height(b) : box_height(b);
3434    halfword depth = cur_list.prev_depth;
3435    if (properties) {
3436        scaled d = glue_amount(properties->baseline_skip) - depth - height;
3437        if (d < properties->line_skip_limit) {
3438            p = tex_new_glue_node(properties->line_skip, line_skip_glue);
3439        } else {
3440            p = tex_new_glue_node(properties->baseline_skip, baseline_skip_glue);
3441            glue_amount(p) = d;
3442        }
3443    } else {
3444        scaled d = glue_amount(baseline_skip_par) - depth - height;
3445        if (d < line_skip_limit_par) {
3446            p = tex_new_param_glue_node(line_skip_code, line_skip_glue);
3447        } else {
3448            p = tex_new_param_glue_node(baseline_skip_code, baseline_skip_glue);
3449            glue_amount(p) = d; /* we keep the stretch and shrink */
3450        }
3451    }
3452    return p;
3453}
3454
3455void tex_append_to_vlist(halfword b, int location, const line_break_properties *properties)
3456{
3457    if (location >= 0) { 
3458        halfword result = null;
3459        halfword next_depth = ignore_depth_criterion_par;
3460        int prev_set = 0;
3461        int check_depth = 0;
3462        if (lmt_append_to_vlist_callback(b, location, cur_list.prev_depth, &result, &next_depth, &prev_set, &check_depth)) {
3463            if (prev_set) {
3464                cur_list.prev_depth = next_depth;
3465            }
3466            if (check_depth && result && (cur_list.prev_depth > ignore_depth_criterion_par)) {
3467                /*tex
3468                    We only deal with a few types and one can always at the \LUA\ end check for some of
3469                    these and decide not to apply the correction.
3470                */
3471                switch (node_type(result)) {
3472                    case hlist_node:
3473                    case vlist_node:
3474                    case rule_node:
3475                        {
3476                            halfword glue = tex_aux_depth_correction(result, properties);
3477                            tex_tail_append(glue);
3478                            break;
3479                        }
3480                }
3481            }
3482            if (result) { 
3483                tex_tail_append_list(result);
3484            }
3485            return;
3486        }
3487    }
3488    if (cur_list.prev_depth > ignore_depth_criterion_par) {
3489        halfword glue = tex_aux_depth_correction(b, properties);
3490        tex_tail_append(glue);
3491    }
3492    tex_tail_append(b);
3493    cur_list.prev_depth = box_depth(b);
3494}
3495
3496/*tex
3497
3498    When |saving_vdiscards| is positive then the glue, kern, and penalty nodes removed by the page
3499    builder or by |\vsplit| from the top of a vertical list are saved in special lists instead of
3500    being discarded.
3501
3502    The |vsplit| procedure, which implements \TEX's |\vsplit| operation, is considerably simpler
3503    than |line_break| because it doesn't have to worry about hyphenation, and because its mission
3504    is to discover a single break instead of an optimum sequence of breakpoints. But before we get
3505    into the details of |vsplit|, we need to consider a few more basic things.
3506
3507    A subroutine called |prune_page_top| takes a pointer to a vlist and returns a pointer to a
3508    modified vlist in which all glue, kern, and penalty nodes have been deleted before the first
3509    box or rule node. However, the first box or rule is actually preceded by a newly created glue
3510    node designed so that the topmost baseline will be at distance |split_top_skip| from the top,
3511    whenever this is possible without backspacing.
3512
3513    When the second argument |s| is |false| the deleted nodes are destroyed, otherwise they are
3514    collected in a list starting at |split_discards|.
3515
3516    Are the prev pointers okay here?
3517
3518*/
3519
3520halfword tex_prune_page_top(halfword p, int s)
3521{
3522    /*tex Lags one step behind |p|. */
3523    halfword prev_p = temp_head;
3524    halfword r = null;
3525    node_next(temp_head) = p;
3526    while (p) {
3527        switch (node_type(p)) {
3528            case hlist_node:
3529            case vlist_node:
3530            case rule_node:
3531                {
3532                    /*tex Insert glue for |split_top_skip| and set |p| to |null|. */
3533                    halfword h = node_type(p) == rule_node ? rule_height(p) : box_height(p);
3534                    halfword q = tex_new_param_glue_node(split_top_skip_code, split_top_skip_glue);
3535                 // node_next(prev_p) = q; 
3536                    tex_couple_nodes(prev_p, q); /* there is no real need to point back to temp */
3537                    tex_couple_nodes(q, p);
3538                    glue_amount(q) = glue_amount(q) > h ? glue_amount(q) - h : 0;
3539                    p = null;
3540                }
3541                break;
3542            case boundary_node:
3543                /* shouldn't we discard */
3544            case whatsit_node:
3545            case mark_node:
3546            case insert_node:
3547                prev_p = p;
3548                p = node_next(prev_p);
3549                break;
3550            case glue_node:
3551            case kern_node:
3552            case penalty_node:
3553                {
3554                    halfword q = p;
3555                    p = node_next(q);
3556                    node_next(q) = null;
3557                    node_next(prev_p) = p;
3558                    if (s) {
3559                        if (lmt_packaging_state.split_discards_head) {
3560                            node_next(r) = q;
3561                        } else {
3562                            lmt_packaging_state.split_discards_head = q;
3563                        }
3564                        r = q;
3565                    } else {
3566                        tex_flush_node_list(q);
3567                    }
3568                }
3569                break;
3570            default:
3571                tex_confusion("pruning page top");
3572                break;
3573        }
3574    }
3575    return node_next(temp_head);
3576}
3577
3578/*tex
3579
3580    The next subroutine finds the best place to break a given vertical list so as to obtain a box
3581    of height~|h|, with maximum depth~|d|. A pointer to the beginning of the vertical list is given,
3582    and a pointer to the optimum breakpoint is returned. The list is effectively followed by a
3583    forced break, i.e., a penalty node with the |eject_penalty|; if the best break occurs at this
3584    artificial node, the value |null| is returned.
3585
3586    An array of six |scaled| distances is used to keep track of the height from the beginning of
3587    the list to the current place, just as in |line_break|. In fact, we use one of the same arrays,
3588    only changing its name to reflect its new significance.
3589
3590    The distance from first active node to |cur_p| is stored in |active_height|.
3591
3592    A global variable |best_height_plus_depth| will be set to the natural size of the box (without
3593    stretching or shrinking) that corresponds to the optimum breakpoint found by |vert_break|. This
3594    value is used by the insertion splitting algorithm of the page builder.
3595
3596    \starttyping
3597    scaled best_height_plus_depth;
3598    \stoptyping
3599
3600 */
3601
3602static halfword tex_aux_vert_badness(scaled goal, scaled active_height[])
3603{
3604    if (active_height[total_advance_amount] < goal) {
3605        if (active_height[total_fi_amount] || active_height[total_fil_amount] || active_height[total_fill_amount] || active_height[total_filll_amount]) {
3606            return 0;
3607        } else {
3608            return tex_badness(goal - active_height[total_advance_amount], active_height[total_stretch_amount]);
3609        }
3610    } else if (active_height[total_advance_amount] - goal > active_height[total_shrink_amount]) {
3611        return awful_bad;
3612    } else {
3613        return tex_badness(active_height[total_advance_amount] - goal, active_height[total_shrink_amount]);
3614    }
3615}
3616
3617static halfword tex_aux_vert_costs(halfword badness, halfword penalty)
3618{
3619    if (badness >= awful_bad) {
3620        return badness; 
3621    } else if (penalty <= eject_penalty) {
3622        return penalty;
3623    } else if (badness < infinite_bad) {
3624        return badness + penalty;
3625    } else {
3626        return deplorable;
3627    }
3628}
3629
3630static void tex_aux_vsplit_callback_initialize(int callback_id, halfword checks, halfword current, scaled height, scaled depth, scaled extra)
3631{
3632    lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "ddNddd->",
3633        initialize_show_vsplit_context,
3634        checks,
3635        current,
3636        height,
3637        depth,
3638        extra
3639    );
3640}
3641
3642static void tex_aux_vsplit_callback_continue(int callback_id, halfword checks, halfword current, scaled height, scaled depth, scaled total, halfword penalty)
3643{
3644    lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "ddNdddd->",
3645        continue_show_vsplit_context,
3646        checks,
3647        current,
3648        height, 
3649        depth,
3650        total,
3651        penalty
3652    );
3653}
3654
3655static void tex_aux_vsplit_callback_check(int callback_id, halfword checks, halfword current, scaled height, scaled depth, scaled total, halfword penalty, halfword badness, halfword costs)
3656{
3657    lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "ddNdddddd->",
3658        check_show_vsplit_context,
3659        checks,
3660        current,
3661        height, 
3662        depth, 
3663        total,
3664        penalty, 
3665        badness,
3666        costs
3667    );
3668}
3669
3670static void tex_aux_vsplit_callback_quit(int callback_id, halfword checks, halfword current, scaled height, scaled depth, scaled total, halfword penalty, halfword badness, halfword costs)
3671{
3672    lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "ddNdddddd->",
3673        quit_show_vsplit_context,
3674        checks,
3675        current,
3676        height, 
3677        depth, 
3678        total,
3679        penalty, 
3680        badness,
3681        costs
3682    );
3683}
3684
3685static void tex_aux_vsplit_callback_wrapup(int callback_id, halfword checks, halfword best)
3686{
3687    lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "ddddddd->",
3688        wrapup_show_vsplit_context,
3689        checks,
3690        best,
3691        lmt_packaging_state.split_last_height,
3692        lmt_packaging_state.split_last_depth,
3693        lmt_packaging_state.split_last_stretch,
3694        lmt_packaging_state.split_last_shrink
3695    );
3696}
3697
3698halfword tex_vert_break(halfword current, scaled height, scaled depth, int callback, scaled extra)
3699{
3700    /*tex
3701        If |p| is a glue node, |type(prev_p)| determines whether |p| is a legal breakpoint, an
3702        initial glue node is not a legal breakpoint.
3703    */
3704    halfword previous = current;
3705    /*tex The to be checked penalty value: */
3706    halfword penalty = 0;
3707    int penalty_state = 0;
3708    /*tex The smallest badness plus penalties found so far: */
3709    halfword least_cost = awful_bad;
3710    /*tex The most recent break that leads to |least_cost|: */
3711    halfword best_place = null;
3712    /*tex The depth of previous box in the list: */
3713    scaled previous_depth = 0;
3714    /*tex The various glue components: */
3715    scaled active_height[10] = { 0 };
3716    scaled passive_height[10] = { 0 };
3717    /*tex For experiments: */
3718    int checks = callback ? vsplit_checks_par : 0;
3719    int callbackid = checks ? lmt_callback_defined(show_vsplit_callback) : 0;
3720    if (callbackid) { 
3721        tex_aux_vsplit_callback_initialize(callbackid, checks, current, height, depth, extra);
3722    }
3723    while (1) {
3724        /*tex
3725            If node |p| is a legal breakpoint, check if this break is the best known, and |goto
3726            done| if |p| is null or if the page-so-far is already too full to accept more stuff.
3727
3728            A subtle point to be noted here is that the maximum depth~|d| might be negative, so
3729            |cur_height| and |prev_dp| might need to be corrected even after a glue or kern node.
3730        */
3731        if (current) {
3732            /*tex
3733                Use node |p| to update the current height and depth measurements; if this node is
3734                not a legal breakpoint, |goto not_found| or |update_heights|, otherwise set |pi|
3735                to the associated penalty at the break.
3736            */
3737            switch (node_type(current)) {
3738                case hlist_node:
3739                case vlist_node:
3740                    /*tex
3741                        If we do this we also need to subtract the dimensions and bubble it up. But
3742                        at least we could inline the inserts.
3743                    */
3744                    /*
3745                    if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_post)) {
3746                        // same code as in page builder
3747                    }
3748                    if (auto_migrating_mode_permitted(auto_migration_mode_par, auto_migrate_pre)) {
3749                        // same code as in page builder
3750                        continue;
3751                    }
3752                    */
3753// if (tex_has_box_option(current, box_option_synchronize)) { 
3754//     scaled total = active_height[total_advance_amount] + previous_depth + box_height(current) - top_skip_par ;
3755//     int lines = (total - 65536)/ glue_amount(baseline_skip_par); /* todo: take from par */
3756//     scaled target = (lines + 1) * glue_amount(baseline_skip_par);
3757//     scaled delta = target - total;
3758//     printf(">>> total %f, lines %i, target %f, delta %f\n",total/65536.0,lines,target/65536.0,delta/65536.0);
3759//     box_height(current) += delta;
3760// }
3761                    active_height[total_advance_amount] += previous_depth + box_height(current);
3762                    previous_depth = box_depth(current);
3763                    goto NOT_FOUND;
3764                case rule_node:
3765                    active_height[total_advance_amount] += previous_depth + rule_height(current);
3766                    previous_depth = rule_depth(current);
3767                    goto NOT_FOUND;
3768                case boundary_node:
3769                case whatsit_node:
3770                    goto NOT_FOUND;
3771                case glue_node:
3772                    if (precedes_break(previous)) {
3773                 /* if (precedes_break(node_prev(current)) { */ /* also ok */
3774                        penalty = 0;
3775                        break;
3776                    } else {
3777                        goto UPDATE_HEIGHTS;
3778                    }
3779                case kern_node:
3780                    if (node_next(current) && node_type(node_next(current)) == glue_node) {
3781                        penalty = 0;
3782                        break;
3783                    } else {
3784                        goto UPDATE_HEIGHTS;
3785                    }
3786                case penalty_node:
3787                    penalty = penalty_amount(current);
3788                    if (tex_has_penalty_option(current, penalty_option_widowed)) { 
3789                            penalty_state = penalty_option_widowed;
3790                    } else if (tex_has_penalty_option(current, penalty_option_clubbed)) { 
3791                            penalty_state = penalty_option_clubbed;
3792                    } else { 
3793                            penalty_state = penalty_option_normal;
3794                    }
3795                    break;
3796                case mark_node:
3797                case insert_node:
3798                    goto NOT_FOUND;
3799                default:
3800                    tex_confusion("vertical break");
3801                    break;
3802            }
3803        } else {
3804            penalty = eject_penalty;
3805        }
3806        /*tex
3807            Check if node |p| is a new champion breakpoint; then |goto done| if |p| is a forced
3808            break or if the page-so-far is already too full.
3809        */
3810        if (penalty < infinite_penalty) {
3811            /*tex Compute the badness, |b|, using |awful_bad| if the box is too full. */
3812            int badness = tex_aux_vert_badness(height, active_height);
3813            int costs = tex_aux_vert_costs(badness, penalty);
3814            if (badness == awful_bad && penalty_state) {
3815                int b = tex_aux_vert_badness(height + extra, active_height);
3816                int c = tex_aux_vert_costs(b, penalty);
3817            //  printf("%i %i / %i %i",badness,b,costs,c);
3818                badness = b; 
3819                costs = c; 
3820                height += extra;
3821                penalty_state = penalty_option_normal;
3822                extra = 0;
3823            } 
3824            if (costs <= least_cost) {
3825                best_place = current;
3826                least_cost = costs;
3827                for (int i = total_advance_amount; i <= total_stretch_amount; i++) {
3828                    passive_height[i] = active_height[i];
3829                }
3830                lmt_packaging_state.best_height_plus_depth = passive_height[total_advance_amount];
3831                /*tex 
3832                    Here's a patch suggested by DEK related to glue in inserts that needs to be taken 
3833                    into account. That patch is not applied to regular \TEX\ for compatibility reasons.
3834                */
3835                if (lmt_packaging_state.best_height_plus_depth > (height + previous_depth) && costs < awful_bad) { 
3836                    lmt_packaging_state.best_height_plus_depth = height + previous_depth;
3837                }
3838            } else { 
3839                    /* do we need to adapt h plus d */
3840            }
3841            if ((costs == awful_bad) || (penalty <= eject_penalty)) {
3842                if (callbackid) { 
3843                    tex_aux_vsplit_callback_quit(callbackid, checks, current, active_height[total_advance_amount], previous_depth, passive_height[total_advance_amount], penalty, badness, costs);
3844                }
3845                goto WRAPUP;
3846            } else { 
3847                if (callbackid) { 
3848                    tex_aux_vsplit_callback_check(callbackid, checks, current, active_height[total_advance_amount], previous_depth, passive_height[total_advance_amount], penalty, badness, costs);
3849                }
3850            }
3851        } else { 
3852            if (callbackid) { 
3853                tex_aux_vsplit_callback_continue(callbackid, checks, current, active_height[total_advance_amount], previous_depth, passive_height[total_advance_amount], penalty);
3854            }
3855        }
3856      UPDATE_HEIGHTS:
3857        /*tex
3858            Update the current height and depth measurements with respect to a glue or kern node~|p|.
3859            Vertical lists that are subject to the |vert_break| procedure should not contain infinite
3860            shrinkability, since that would permit any amount of information to fit on one page. We 
3861            only end up here for glue and kern nodes.
3862        */
3863        switch(node_type(current)) {
3864            case kern_node:
3865                active_height[total_advance_amount] += previous_depth + kern_amount(current);
3866                previous_depth = 0;
3867                goto KEEP_GOING; /* We assume a positive depth. */
3868            case glue_node:
3869                active_height[total_stretch_amount + glue_stretch_order(current)] += glue_stretch(current);
3870                active_height[total_shrink_amount] += glue_shrink(current);
3871                if (glue_shrink_order(current) != normal_glue_order  && glue_shrink(current)) {
3872                    /*tex 
3873                        Other engines now can bypass this error message but we don't follow that 
3874                        approach here because we're more into \quote {mechanmism control}. We could 
3875                        however, if really needed, make it |warning_error_type|. 
3876                    */
3877                    tex_handle_error(
3878                        normal_error_type,
3879                        "Infinite glue shrinkage found in box being split",
3880                        "The box you are \\vsplitting contains some infinitely shrinkable glue, e.g.,\n"
3881                        "'\\vss' or '\\vskip 0pt minus 1fil'. Such glue doesn't belong there; but you can\n"
3882                        "safely proceed, since the offensive shrinkability has been made finite."
3883                    );
3884                    glue_shrink_order(current) = normal_glue_order;
3885                }
3886                active_height[total_advance_amount] += previous_depth + glue_amount(current);
3887                previous_depth = 0;
3888                goto KEEP_GOING; /* We assume a positive depth. */
3889        }
3890      NOT_FOUND:
3891        if (previous_depth > depth) {
3892            active_height[total_advance_amount] += previous_depth - depth;
3893            previous_depth = depth;
3894        }
3895      KEEP_GOING:
3896        previous = current;
3897        current = node_next(current);
3898    }
3899  WRAPUP:
3900    lmt_packaging_state.split_last_height  = passive_height[total_advance_amount];
3901    lmt_packaging_state.split_last_depth   = previous_depth; /* yes or no */
3902    lmt_packaging_state.split_last_stretch = passive_height[total_stretch_amount];
3903    lmt_packaging_state.split_last_shrink  = passive_height[total_shrink_amount];
3904    if (callbackid) { 
3905        tex_aux_vsplit_callback_wrapup(callbackid, checks, best_place);
3906    }
3907    return best_place; 
3908}
3909
3910/*tex
3911
3912    Now we are ready to consider |vsplit| itself. Most of its work is accomplished by the two
3913    subroutines that we have just considered.
3914
3915    Given the number of a vlist box |n|, and given a desired page height |h|, the |vsplit|
3916    function finds the best initial segment of the vlist and returns a box for a page of height~|h|.
3917    The remainder of the vlist, if any, replaces the original box, after removing glue and penalties
3918    and adjusting for |split_top_skip|. Mark nodes in the split-off box are used to set the values
3919    of |split_first_mark| and |split_bot_mark|; we use the fact that |split_first_mark(x) = null| if
3920    and only if |split_bot_mark(x) = null|.
3921
3922    The original box becomes \quote {void} if and only if it has been entirely extracted. The
3923    extracted box is \quote {void} if and only if the original box was void (or if it was,
3924    erroneously, an hlist box).
3925
3926    Extract a page of height |h| from box |n|:
3927*/
3928
3929halfword tex_vsplit(halfword n, scaled h, int m)
3930{
3931    /*tex the box to be split */
3932    halfword v = box_register(n);
3933    tex_flush_node_list(lmt_packaging_state.split_discards_head);
3934    lmt_packaging_state.split_discards_head = null;
3935    for (halfword i = 0; i <= lmt_mark_state.mark_data.ptr; i++) {
3936        tex_delete_mark(i, split_first_mark_code);
3937        tex_delete_mark(i, split_bot_mark_code);
3938    }
3939    /*tex Dispense with trivial cases of void or bad boxes. */
3940    if (! v) {
3941        return null;
3942    } else if (node_type(v) != vlist_node) {
3943        tex_handle_error(
3944            normal_error_type,
3945            "\\vsplit needs a \\vbox",
3946            "The box you are trying to split is an \\hbox. I can't split such a box, so I''ll\n"
3947            "leave it alone."
3948        );
3949        return null;
3950    } else {
3951        /*tex points to where the break occurs */
3952        halfword q = tex_vert_break(box_list(v), h, split_max_depth_par, 1, split_extra_height_par);
3953        /*tex
3954
3955            Look at all the marks in nodes before the break, and set the final link to |null| at
3956            the break. It's possible that the box begins with a penalty node that is the quote
3957            {best} break, so we must be careful to handle this special case correctly.
3958
3959        */
3960        halfword p = box_list(v);
3961        /*tex The direction of the box to be split, obsolete! */
3962        int vdir = box_dir(v);
3963        if (p == q) {
3964            box_list(v) = null;
3965        } else {
3966            while (1) {
3967                if (node_type(p) == mark_node) {
3968                    tex_update_split_mark(p);
3969                }
3970                if (node_next(p) == q) {
3971                    node_next(p) = null;
3972                    break;
3973                } else {
3974                    p = node_next(p);
3975                }
3976            }
3977        }
3978        q = tex_prune_page_top(q, saving_vdiscards_par > 0);
3979        p = box_list(v);
3980        box_list(v) = null;
3981        tex_flush_node(v);
3982        if (q) {
3983            box_register(n) = tex_filtered_vpack(q, 0, packing_additional, max_depth_par, split_keep_group, vdir, 0, 0, 0, holding_none_option, NULL);
3984        } else {
3985            /*tex The |eq_level| of the box stays the same. */
3986            box_register(n) = null;
3987        }
3988        return tex_filtered_vpack(p, m == packing_additional ? 0 : h, m, max_depth_par, split_off_group, vdir, 0, 0, 0, holding_none_option, NULL);
3989    }
3990}
3991
3992/*tex
3993
3994    Now that we can see what eventually happens to boxes, we can consider the first steps in their
3995    creation. The |begin_box| routine is called when |box_context| is a context specification,
3996    |cur_chr| specifies the type of box desired, and |cur_cmd=make_box|.
3997
3998*/
3999
4000void tex_begin_box(int boxcontext, scaled shift, halfword slot, halfword callback, halfword leaders)
4001{
4002    halfword code = cur_chr;
4003    halfword boxnode = null; /*tex Aka |cur_box|. */
4004    switch (code) {
4005        case box_code:
4006            {
4007                halfword n = tex_scan_box_register_number();
4008                boxnode = box_register(n);
4009                /*tex The box becomes void, at the same level. */
4010                box_register(n) = null;
4011                break;
4012            }
4013        case copy_code:
4014            {
4015                halfword n = tex_scan_box_register_number();
4016                boxnode = box_register(n) ? tex_copy_node(box_register(n)) : null;
4017                break;
4018            }
4019     /* case unpack_code: */
4020     /*     break;        */
4021         case last_box_code:
4022            /*tex
4023                If the current list ends with a box node, delete it from the list and make |boxnode|
4024                point to it; otherwise set |boxnode := null|.
4025            */
4026            boxnode = null;
4027            if (is_m_mode(cur_list.mode)) {
4028                tex_you_cant_error(
4029                    "Sorry; this \\lastbox will be void."
4030                );
4031            } else if (cur_list.mode == vmode && cur_list.head == cur_list.tail) {
4032                tex_you_cant_error(
4033                    "Sorry...I usually can't take things from the current page.\n"
4034                    "This \\lastbox will therefore be void."
4035                );
4036            } else if (cur_list.head != cur_list.tail) {
4037                switch (node_type(cur_list.tail)) {
4038                    case hlist_node:
4039                    case vlist_node:
4040                        {
4041                            /*tex Remove the last box */
4042                            halfword q = node_prev(cur_list.tail);
4043                            if (! q || node_next(q) != cur_list.tail) {
4044                                q = cur_list.head;
4045                                while (node_next(q) != cur_list.tail)
4046                                    q = node_next(q);
4047                            }
4048                            tex_uncouple_node(cur_list.tail);
4049                            boxnode = cur_list.tail;
4050                            box_shift_amount(boxnode) = 0;
4051                            cur_list.tail = q;
4052                            node_next(cur_list.tail) = null;
4053                        }
4054                        break;
4055                }
4056            }
4057            break;
4058        case tsplit_code:
4059        case vsplit_code:
4060        case dsplit_code:
4061            {
4062                /*tex
4063                    Split off part of a vertical box, make |boxnode| point to it. Here we deal with
4064                    things like |\vsplit 13 to 100pt|. Maybe todo: just split off one line.
4065
4066                */
4067                halfword mode = packing_exactly ;
4068                halfword index = tex_scan_box_register_number();
4069                halfword size = 0;
4070                halfword attrlist = null;
4071                while (1) {
4072                    switch (tex_scan_character("atuATU", 0, 1, 0)) {
4073                        case 0:
4074                            goto DONE1;
4075                        case 'a': case 'A':
4076                            if (tex_scan_mandate_keyword("attr", 1)) {
4077                                attrlist = tex_scan_attribute(attrlist);
4078                            }
4079                            break;
4080                        case 't': case 'T':
4081                            if (tex_scan_mandate_keyword("to", 1)) {
4082                                mode = packing_exactly;
4083                                size = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
4084                            }
4085                            break;
4086                        case 'u': case 'U':
4087                            if (tex_scan_mandate_keyword("upto", 1)) {
4088                                mode = packing_additional;
4089                                size = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
4090                            }
4091                            break;
4092                        default:
4093                            tex_aux_show_keyword_error("attr|upto|to");
4094                            goto DONE1;
4095                    }
4096                }
4097              DONE1:
4098                boxnode = tex_vsplit(index, size, mode);
4099                tex_aux_set_vnature(boxnode, code);
4100                if (attrlist) { 
4101                    tex_attach_attribute_list_attribute(boxnode, attrlist);
4102                }
4103            }
4104            break;
4105        case vbalance_code:
4106            {
4107                /* Maybe also use options here. */
4108                halfword index = tex_scan_box_register_number();
4109                halfword mode = packing_exactly;
4110                halfword trial = 0;
4111                while (1) {
4112                    switch (tex_scan_character("aetAET", 0, 1, 0)) {
4113                        case 0:
4114                            goto BALANCE;
4115                        case 'e': case 'E':
4116                            if (tex_scan_mandate_keyword("exactly", 1)) {
4117                                mode = packing_exactly;
4118                            }
4119                            break;
4120                        case 'a': case 'A':
4121                            if (tex_scan_mandate_keyword("additional", 1)) {
4122                                mode = packing_additional;
4123                            }
4124                            break;
4125                        case 't': case 'T':
4126                            if (tex_scan_mandate_keyword("trial", 1)) {
4127                             /* mode = packing_trial; */
4128                                trial = 1;
4129                            }
4130                            break;
4131                        default:
4132                            tex_aux_show_keyword_error("exactly|additional|trial");
4133                            goto BALANCE;
4134                    }
4135                }
4136              BALANCE:
4137                boxnode = tex_vbalance(index, mode, trial);
4138            }
4139            break;
4140        case vbalanced_box_code:
4141        case vbalanced_top_code:
4142            {
4143                halfword index = tex_scan_box_register_number();
4144                boxnode = tex_vbalanced(index);
4145            }
4146            break;
4147        case vbalanced_insert_code:
4148            {
4149                halfword index = tex_scan_box_register_number();
4150                halfword insert = -1;
4151                halfword options = 0;
4152                while (1) {
4153                    switch (tex_scan_character("diDI", 0, 1, 0)) {
4154                        case 0:
4155                            goto GETINSERT;
4156                        case 'd': case 'D':
4157                            if (tex_scan_mandate_keyword("descend", 1)) {
4158                                options |= balance_insert_descend;
4159                            }
4160                            break;
4161                        case 'i': case 'I':
4162                            if (tex_scan_mandate_keyword("index", 1)) {
4163                                insert = tex_scan_insert_index();
4164                            }
4165                            break;
4166                        default:
4167                            tex_aux_show_keyword_error("descend|index");
4168                            goto GETINSERT;
4169                    }
4170                }
4171              GETINSERT:
4172                if (insert < 0) {
4173                    insert = tex_scan_insert_index();
4174                }
4175                boxnode = tex_vbalanced_insert(index, insert, options);
4176            }
4177            break;
4178        case vbalanced_discard_code:
4179            {
4180                halfword index = tex_scan_box_register_number();
4181                halfword options = 0;
4182                while (1) {
4183                    switch (tex_scan_character("drDR", 0, 1, 0)) {
4184                        case 0:
4185                            goto DISCARD;
4186                        case 'd': case 'D':
4187                            if (tex_scan_mandate_keyword("descend", 1)) {
4188                                options |= balance_discard_descend;
4189                            }
4190                            break;
4191                        case 'r': case 'R':
4192                            if (tex_scan_mandate_keyword("remove", 1)) {
4193                                options |= balance_discard_remove;
4194                            }
4195                            break;
4196                        default:
4197                            tex_aux_show_keyword_error("remove");
4198                            goto DISCARD;
4199                    }
4200                }
4201              DISCARD:
4202                tex_vbalanced_discard(index, options);
4203            }
4204            break;
4205        case vbalanced_deinsert_code:
4206            {
4207                halfword index = tex_scan_box_register_number();
4208                halfword options = 0;
4209                while (1) {
4210                    switch (tex_scan_character("dfDF", 0, 1, 0)) {
4211                        case 0:
4212                            goto DEINSERT;
4213                        case 'd': case 'D':
4214                            if (tex_scan_mandate_keyword("descend", 1)) {
4215                                options |= balance_deinsert_descend;
4216                            }
4217                            break;
4218                        case 'f': case 'F':
4219                            if (tex_scan_mandate_keyword("force", 1)) {
4220                                switch (tex_scan_character("dhDH", 0, 1, 0)) {
4221                                    case 'd': case 'D':
4222                                        if (tex_scan_mandate_keyword("forcedepth", 6)) {
4223                                            options |= balance_deinsert_linedepth;
4224                                        }
4225                                        break;
4226                                    case 'h': case 'H':
4227                                        if (tex_scan_mandate_keyword("forceheight", 6)) {
4228                                            options |= balance_deinsert_lineheight;
4229                                        }
4230                                        break;
4231                                    default:
4232                                        tex_aux_show_keyword_error("forcedepth|forceheight");
4233                                        goto DEINSERT;
4234                                }
4235                            }
4236                            break;
4237                        default:
4238                            tex_aux_show_keyword_error("forcedepth|forceheight");
4239                            goto DEINSERT;
4240                    }
4241                }
4242              DEINSERT:
4243                tex_vbalanced_deinsert(index, options);
4244            }
4245            break;
4246        case vbalanced_reinsert_code:
4247            {
4248                halfword index = tex_scan_box_register_number();
4249                halfword options = 0;
4250                while (1) {
4251                    switch (tex_scan_character("dD", 0, 1, 0)) {
4252                        case 0:
4253                            goto REINSERT;
4254                        case 'd': case 'D':
4255                            if (tex_scan_mandate_keyword("descend", 1)) {
4256                                options |= balance_reinsert_descend;
4257                            }
4258                            break;
4259                        default:
4260                            tex_aux_show_keyword_error("descend");
4261                            goto REINSERT;
4262                    }
4263                }
4264              REINSERT:
4265                tex_vbalanced_reinsert(index, options);
4266            }
4267            break;
4268        case flush_mvl_box_code:
4269            {
4270                /*tex Scanning might move to the flush routine. */
4271                halfword index = tex_scan_integer(0, NULL, NULL);
4272                boxnode = tex_flush_mvl(index);
4273            }
4274            break;
4275        case insert_box_code:
4276        case insert_copy_code:
4277            {
4278                halfword index = tex_scan_integer(0, NULL, NULL);
4279                if (tex_valid_insert_id(index)) {
4280                    boxnode = tex_get_insert_content(index);
4281                    if (boxnode) {
4282                        if (node_type(boxnode) == vlist_node) {
4283                            if (code == insert_copy_code) {
4284                                boxnode = tex_copy_node(boxnode);
4285                            } else {
4286                                tex_set_insert_content(index, null);
4287                            }
4288                        } else {
4289                            tex_set_insert_content(index, null);
4290                            /* error, maybe migration list  */
4291                        }
4292                    }
4293                }
4294                break;
4295            }
4296        case local_left_box_box_code:
4297            {
4298                boxnode = tex_get_local_boxes(local_left_box_code);
4299                break;
4300            }
4301        case local_right_box_box_code:
4302            {
4303                boxnode = tex_get_local_boxes(local_right_box_code);
4304                break;
4305            }
4306        case local_middle_box_box_code:
4307            {
4308                boxnode = tex_get_local_boxes(local_middle_box_code);
4309                break;
4310            }
4311       /*tex
4312
4313            Initiate the construction of an hbox or vbox, then |return|. Here is where we
4314            enter restricted horizontal mode or internal vertical mode, in order to make a
4315            box. The juggling with codes and addition or subtraction was somewhat messy.
4316
4317        */
4318        default:
4319            {
4320                quarterword direction;
4321                int justpack = 0;
4322                quarterword group = vbox_group;
4323                int mode = vmode;
4324                int adjusted = 0;
4325                switch (cur_list.mode) {
4326                    case vmode:
4327                    case internal_vmode:
4328                        direction = dir_lefttoright;
4329                        if (boxcontext == direct_box_flag) {
4330                            adjusted = 1;
4331                        }
4332                        break;
4333                    case hmode:
4334                    case restricted_hmode:
4335                        direction = (singleword) text_direction_par;
4336                        break;
4337                    case mmode:
4338                    case inline_mmode:
4339                        direction = (singleword) math_direction_par;
4340                        break;
4341                    default: 
4342                        direction = direction_unknown;
4343                        break;
4344                }
4345                switch (code) {
4346                    case tpack_code:
4347                     // mode = vmode; 
4348                        justpack = 1;
4349                        group = vtop_group;
4350                        break;
4351                    case vpack_code:
4352                     // mode = vmode; 
4353                        justpack = 1;
4354                     // group = vbox_group;
4355                        break;
4356                    case hpack_code:
4357                        mode = hmode; 
4358                        justpack = 1;
4359                        group = adjusted ? adjusted_hbox_group : hbox_group;
4360                        break;
4361                    case dpack_code:
4362                     // mode = vmode; 
4363                        justpack = 1;
4364                        group = dbox_group;
4365                        break;
4366                    case vtop_code:
4367                     // mode = vmode;
4368                     // justpack = 0;
4369                        group = vtop_group;
4370                        break;
4371                    case vbox_code:
4372                     // mode = vmode;
4373                     // justpack = 0;
4374                     // group = vbox_group;
4375                        break;
4376                    case hbox_code:   
4377                        mode = hmode;
4378                     // justpack = 0;
4379                        group = adjusted ? adjusted_hbox_group : hbox_group;
4380                        break;
4381                    case dbox_code:
4382                     // mode = vmode;
4383                     // justpack = 0;
4384                        group = dbox_group;
4385                        break;
4386                }
4387                tex_aux_scan_full_spec(boxcontext, group, direction, justpack, shift, slot, callback, leaders);
4388                tex_normal_paragraph(vmode_par_context);
4389                tex_push_nest();
4390                update_tex_internal_dir_state(0);
4391                cur_list.mode = - mode;
4392                if (mode == vmode) {
4393                    cur_list.prev_depth = ignore_depth_criterion_par;
4394                    if (every_vbox_par) {
4395                        tex_begin_token_list(every_vbox_par, every_vbox_text);
4396                    }
4397                } else {
4398                    cur_list.space_factor = default_space_factor;
4399                    if (every_hbox_par) {
4400                        tex_begin_token_list(every_hbox_par, every_hbox_text);
4401                    }
4402                }
4403                return;
4404            }
4405    }
4406    /*tex In simple cases, we use the box immediately. */
4407    tex_box_end(boxcontext, boxnode, shift, unset_noad_class, slot, callback, leaders);
4408}
4409
4410/*tex
4411
4412    0x0001 : non data par nodes
4413    0x0002 : direction noded
4414    0x0004 : indentation nodes
4415
4416*/
4417
4418int tex_is_effectively_empty(halfword n, halfword options) 
4419{
4420    while (n) { 
4421        switch (node_type(n)) { 
4422            case par_node:
4423                if (options & effective_empty_option_par) { 
4424                    switch (node_subtype(n)) { 
4425                        case vmode_par_par_subtype:
4426                     // case local_box_par_subtype:
4427                        case hmode_par_par_subtype:
4428                        case parameter_par_subtype:
4429                     // case local_break_par_subtype:
4430                     // case math_par_subtype:   
4431                            break;
4432                        default:
4433                            return 0;
4434                    }
4435                    break;
4436                } else { 
4437                    return 0;
4438                }
4439            case dir_node:
4440                if (options & effective_empty_option_dir) { 
4441                    break;
4442                } else { 
4443                    return 0;
4444                }
4445            case glue_node:
4446                if (options & effective_empty_option_indent_glue) { 
4447                    switch (node_subtype(n)) {
4448                        case indent_skip_glue:
4449                        case par_init_left_skip_glue:
4450                        case par_init_right_skip_glue:
4451                            break;
4452                        default:
4453                            return 0;
4454                    }
4455                    break;
4456                } else {
4457                    return 0;
4458                }
4459        case hlist_node:
4460                if (options & effective_empty_option_indent_list) { 
4461                    switch (node_subtype(n)) {
4462                        case indent_list:
4463                            break;
4464                        default:
4465                            return 0;
4466                    }
4467                    break;
4468                } else { 
4469                    return 0;
4470                }
4471         // case boundary_node:
4472         //     /* maybe some */
4473         //    return 0;
4474            default: 
4475                return 0;
4476        }
4477        n = node_next(n);
4478    }
4479    return 1;
4480}