texinserts.c /size: 26 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    In traditional \TEX\ inserts are implemented using a quadruple of box, dimen, count and skip
10    registers. This means that the allocate macro |\newinsert| as well as the other allocators
11    have to keep a range of registers free. In \CONTEXT\ (\MKII\ and \MKIV) for instance the
12    indices 132 upto 254 are reserved for that.
13
14    When pondering about improvements this implementation detail always puts some strains on
15    the possible solutions and it is for that reason that an alternative code path is present,
16    one that keep the relevant data in dedicated data structures. When that got implemented all
17    accessors ended up here. Most were already abstracted anyway. For now it means that the old
18    interface still works (and is default). By setting the |\insertmode| to 2 the alternative
19    path is chosen. For practical reasons the first time an insert is used that value gets
20    frozen; a mixed approach was too messy.
21
22    Actually the new variant, which is tagged |class| instead of |index|, also better suits the
23    extended box model. There is access to the basic three dimension but that's all. One can wrap
24    in a box and mess with others but doing that with the boxes inserts makes no sense because
25    the output routine expects simple boxes.
26
27    A side effect is of course that we now have more primitives, starting with |\insert...| and
28    also helpers at the \LUA\ end. A few more will follow and likely some enhancements will show
29    up too.
30
31    In this new mode we also store the floatingpenalty and maxdepth so these can now differ per
32    class. They were already stored in the node, but this way we don't need to set the shared
33    variable every time we do an insert.
34
35*/
36
37insert_state_info lmt_insert_state = {
38    .inserts     = NULL,
39    .insert_data = {
40        .minimum   = min_insert_size,
41        .maximum   = max_insert_size,
42        .size      = memory_data_unset,
43        .step      = stp_insert_size,
44        .allocated = 0,
45        .itemsize  = sizeof(insert_record),
46        .top       = 0,
47        .ptr       = 0,
48        .initial   = memory_data_unset,
49        .offset    = 0,
50        .extra     = 0, 
51    },
52    .mode        = unset_insert_mode,
53    .storing     = 0,
54};
55
56typedef enum saved_insert_entries {
57    saved_insert_index_entry    = 0, 
58    saved_insert_data_entry     = 0, 
59    saved_insert_callback_entry = 0, 
60    saved_insert_n_of_records   = 1,
61} saved_insert_entries;
62
63# define saved_insert_index    saved_value_1(saved_insert_index_entry)
64# define saved_insert_data     saved_value_2(saved_insert_data_entry)
65# define saved_insert_callback saved_value_3(saved_insert_callback_entry)
66
67static inline void saved_inserts_initialize(void)
68{
69    saved_type(0) = saved_record_0;
70    saved_record(0) = insert_save_type;
71}
72
73void tex_show_insert_group(void)
74{
75    tex_print_str_esc("insert");
76    tex_print_int(saved_insert_index);
77}
78
79int tex_show_insert_record(void)
80{
81    tex_print_str("insert ");
82    switch (saved_type(0)) { 
83       case saved_record_0:
84            tex_print_format("index %i", saved_insert_index);       // saved_value_1(0)
85            break;
86       case saved_record_1:
87            tex_print_format("data %i", saved_insert_data);         // saved_value_2(0)  
88            break;
89       case saved_record_2:
90            tex_print_format("callback %i", saved_insert_callback); // saved_value_3(0)
91            break;
92        default: 
93            return 0;
94    }
95    return 1;
96}
97
98void tex_initialize_inserts(void)
99{
100    insert_record *tmp = aux_allocate_clear_array(sizeof(insert_record), lmt_insert_state.insert_data.minimum, 1);
101    if (tmp) {
102        lmt_insert_state.inserts = tmp;
103        lmt_insert_state.insert_data.allocated = lmt_insert_state.insert_data.minimum;
104        lmt_insert_state.insert_data.top = lmt_insert_state.insert_data.minimum;
105        lmt_insert_state.insert_data.ptr = 0;
106    } else {
107        tex_overflow_error("inserts", lmt_insert_state.insert_data.minimum);
108    }
109}
110
111/*tex
112    This one is not sparse but we don't have many inserts so we're okay. I need to check the 0/1
113    offsets here.
114*/
115
116int tex_valid_insert_id(halfword n)
117{
118    switch (lmt_insert_state.mode) {
119        case index_insert_mode:
120            return (n >= 0 && n <= max_box_register_index);
121        case class_insert_mode:
122            if (n <= 0) {
123                tex_handle_error(
124                    normal_error_type,
125                    "In \\insertmode 2 you can't use zero as index.",
126                    NULL
127                );
128            } else if (n <= lmt_insert_state.insert_data.ptr) {
129                return 1;
130            } else if (n < lmt_insert_state.insert_data.top) {
131                lmt_insert_state.insert_data.ptr = n;
132                return 1;
133            } else if (n < lmt_insert_state.insert_data.maximum && lmt_insert_state.insert_data.top < lmt_insert_state.insert_data.maximum) {
134                insert_record *tmp;
135                int top = n + lmt_insert_state.insert_data.step;
136                if (top > lmt_insert_state.insert_data.maximum) {
137                    top = lmt_insert_state.insert_data.maximum;
138                }
139                tmp = aux_reallocate_array(lmt_insert_state.inserts, sizeof(insert_record), top, 1); // 1 slack
140                if (tmp) {
141                    size_t extra = ((size_t) top - lmt_insert_state.insert_data.top) * sizeof(insert_record);
142                    memset(&tmp[lmt_insert_state.insert_data.top + 1], 0, extra);
143                 // memset(&tmp[lmt_insert_state.insert_data.top], 0, extra);
144                    lmt_insert_state.inserts = tmp;
145                    lmt_insert_state.insert_data.allocated = top;
146                    lmt_insert_state.insert_data.top = top;
147                    lmt_insert_state.insert_data.ptr = n;
148                    return 1;
149                }
150            }
151            tex_overflow_error("inserts", lmt_insert_state.insert_data.maximum);
152    }
153    return 0;
154}
155
156scaled tex_get_insert_limit(halfword i)
157{
158    if (tex_valid_insert_id(i)) {
159        return lmt_insert_state.mode == index_insert_mode ? insert_maxheight(i) : lmt_insert_state.inserts[i].limit;
160    } else {
161        return 0;
162    }
163}
164
165halfword tex_get_insert_multiplier(halfword i)
166{
167    if (tex_valid_insert_id(i)) {
168        return lmt_insert_state.mode == index_insert_mode ? insert_multiplier(i) : lmt_insert_state.inserts[i].multiplier;
169    } else {
170        return 0;
171    }
172}
173
174halfword tex_get_insert_penalty(halfword i)
175{
176    if (tex_valid_insert_id(i)) {
177        return lmt_insert_state.mode == index_insert_mode ? floating_penalty_par : lmt_insert_state.inserts[i].penalty;
178    } else {
179        return 0;
180    }
181}
182
183halfword tex_get_insert_maxdepth(halfword i)
184{
185    if (tex_valid_insert_id(i)) {
186        return lmt_insert_state.mode == index_insert_mode ? split_max_depth_par : lmt_insert_state.inserts[i].maxdepth;
187    } else {
188        return 0;
189    }
190}
191
192halfword tex_get_insert_distance(halfword i)
193{
194    if (tex_valid_insert_id(i)) {
195        return lmt_insert_state.mode == index_insert_mode ? insert_distance(i) : lmt_insert_state.inserts[i].distance;
196    } else {
197        return 0;
198    }
199}
200
201static inline halfword tex_aux_insert_box(halfword i)
202{
203    if (tex_valid_insert_id(i)) {
204        return lmt_insert_state.mode == index_insert_mode ? insert_content(i) : lmt_insert_state.inserts[i].content;
205    } else {
206        return null;
207    }
208}
209
210scaled tex_get_insert_height(halfword i)
211{
212    halfword b = tex_aux_insert_box(i);
213    return b ? box_height(b) : 0;
214}
215
216scaled tex_get_insert_depth(halfword i)
217{
218    halfword b = tex_aux_insert_box(i);
219    return b ? box_depth(b) : 0;
220}
221
222scaled tex_get_insert_width(halfword i)
223{
224    halfword b = tex_aux_insert_box(i);
225    return b ? box_width(b) : 0;
226}
227
228halfword tex_get_insert_content(halfword i)
229{
230    return tex_aux_insert_box(i);
231}
232
233scaled tex_get_insert_line_height(halfword i) 
234{
235    if (lmt_insert_state.mode == class_insert_mode && tex_valid_insert_id(i)) {
236        return lmt_insert_state.inserts[i].lineheight;
237    } else { 
238        return 0;
239    }
240}
241
242scaled tex_get_insert_line_depth(halfword i) 
243{
244    if (lmt_insert_state.mode == class_insert_mode && tex_valid_insert_id(i)) {
245        return lmt_insert_state.inserts[i].linedepth;
246    } else { 
247        return 0;
248    }
249}
250scaled tex_get_insert_stretch(halfword i) 
251{
252    if (lmt_insert_state.mode == class_insert_mode && tex_valid_insert_id(i)) {
253        return lmt_insert_state.inserts[i].stretch;
254    } else { 
255        return 0;
256    }
257}
258
259scaled tex_get_insert_shrink(halfword i) 
260{
261    if (lmt_insert_state.mode == class_insert_mode && tex_valid_insert_id(i)) {
262        return lmt_insert_state.inserts[i].shrink;
263    } else { 
264        return 0;
265    }
266}
267
268halfword tex_get_insert_storage(halfword i)
269{
270    if (lmt_insert_state.mode == class_insert_mode && tex_valid_insert_id(i)) {
271        return has_insert_option(i, insert_option_storing);
272    } else {
273        return 0;
274    }
275}
276
277void tex_set_insert_limit(halfword i, scaled v)
278{
279    if (tex_valid_insert_id(i)) {
280        switch (lmt_insert_state.mode) {
281            case index_insert_mode: insert_maxheight(i) = v; break;
282            case class_insert_mode: lmt_insert_state.inserts[i].limit = v; break;
283        }
284    }
285}
286
287void tex_set_insert_multiplier(halfword i, halfword v) 
288{
289    if (tex_valid_insert_id(i)) {
290        switch (lmt_insert_state.mode) {
291            case index_insert_mode: insert_multiplier(i) = v; break;
292            case class_insert_mode: lmt_insert_state.inserts[i].multiplier = v; break;
293        }
294    }
295}
296
297void tex_set_insert_penalty(halfword i, halfword v) 
298{
299    if (tex_valid_insert_id(i) && lmt_insert_state.mode == class_insert_mode) {
300        lmt_insert_state.inserts[i].options = set_insert_option(lmt_insert_state.inserts[i].options, insert_option_penalty);
301        lmt_insert_state.inserts[i].penalty = v;
302    }
303}
304
305void tex_set_insert_maxdepth(halfword i, halfword v) 
306{
307    if (tex_valid_insert_id(i) && lmt_insert_state.mode == class_insert_mode) {
308        lmt_insert_state.inserts[i].options = set_insert_option(lmt_insert_state.inserts[i].options, insert_option_maxdepth);
309        lmt_insert_state.inserts[i].maxdepth = v;
310    }
311}
312
313void tex_set_insert_distance(halfword i, halfword v) 
314{
315    if (tex_valid_insert_id(i)) {
316        int d = null;
317        switch (lmt_insert_state.mode) {
318            case index_insert_mode:
319                d = insert_distance(i);
320                insert_distance(i) = v;
321                break;
322            case class_insert_mode:
323                d = lmt_insert_state.inserts[i].distance;
324                lmt_insert_state.inserts[i].distance = v;
325                break;
326        }
327        tex_flush_node(d);
328    }
329}
330
331void tex_set_insert_height(halfword i, scaled v) 
332{
333    halfword b = tex_aux_insert_box(i);
334    if (b) {
335        box_height(b) = v;
336    }
337}
338
339void tex_set_insert_depth(halfword i, scaled v) 
340{
341    halfword b = tex_aux_insert_box(i);
342    if (b) {
343        box_depth(b) = v;
344    }
345}
346
347void tex_set_insert_width(halfword i, scaled v) 
348{
349    halfword b = tex_aux_insert_box(i);
350    if (b) {
351        box_width(b) = v;
352    }
353}
354
355void tex_set_insert_content(halfword i, halfword v) 
356{
357    /* can have old pointer to list */
358    switch (lmt_insert_state.mode) {
359        case index_insert_mode: insert_content(i) = v; break;
360        case class_insert_mode: if (tex_valid_insert_id(i)) { lmt_insert_state.inserts[i].content = v; } break;
361    }
362}
363
364void tex_set_insert_line_height(halfword i, scaled v) 
365{
366    if (lmt_insert_state.mode == class_insert_mode && tex_valid_insert_id(i)) {
367        lmt_insert_state.inserts[i].lineheight = v;
368    }
369}
370
371void tex_set_insert_line_depth(halfword i, scaled v) 
372{
373    if (lmt_insert_state.mode == class_insert_mode && tex_valid_insert_id(i)) {
374        lmt_insert_state.inserts[i].linedepth = v;
375    }
376}
377
378void tex_set_insert_stretch(halfword i, scaled v) 
379{
380    if (lmt_insert_state.mode == class_insert_mode && tex_valid_insert_id(i)) {
381        lmt_insert_state.inserts[i].stretch = v;
382    }
383}
384
385void tex_set_insert_shrink(halfword i, scaled v) {
386    if (lmt_insert_state.mode == class_insert_mode && tex_valid_insert_id(i)) {
387        lmt_insert_state.inserts[i].shrink = v;
388    }
389}
390
391void tex_set_insert_storage(halfword i, halfword v)
392{
393    if (lmt_insert_state.mode == class_insert_mode && tex_valid_insert_id(i)) {
394        lmt_insert_state.inserts[i].options = v
395      ? set_insert_option(lmt_insert_state.inserts[i].options, insert_option_storing)
396      : unset_insert_option(lmt_insert_state.inserts[i].options, insert_option_storing);
397    }
398}
399
400void tex_wipe_insert(halfword i) {
401    if (lmt_insert_state.mode == class_insert_mode && i >= 0 && i <= lmt_insert_state.insert_data.ptr) {
402//  if (lmt_insert_state.mode == class_insert_mode && tex_valid_insert_id(i)) {
403        halfword b = lmt_insert_state.inserts[i].content;
404        if (b) {
405            tex_flush_node(b);
406            lmt_insert_state.inserts[i].content = null;
407        }
408    }
409}
410
411halfword lmt_get_insert_distance(halfword i, int slot)
412{
413    int callback_id = lmt_callback_defined(insert_distance_callback);
414    if (callback_id != 0) {
415        halfword replacement = null;
416        lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dd->N", i, slot, &replacement);
417        if (replacement) {
418            return replacement;
419        } else {
420            halfword distance = null;
421            switch (lmt_insert_state.mode) {
422                case index_insert_mode:
423                    distance = insert_distance(i);
424                    break;
425                case class_insert_mode:
426                    if (tex_valid_insert_id(i)) {
427                        distance = lmt_insert_state.inserts[i].distance;
428                    }
429                    break;
430            }
431            if (distance) {
432                return tex_copy_node(distance);
433            }
434        }
435    }
436    return tex_new_glue_spec_node(null);
437}
438
439halfword tex_get_insert_progress(halfword i)
440{
441    if (tex_valid_insert_id(i)) {
442        halfword p = page_insert_head;
443        while (p && i >= insert_index(node_next(p))) {
444            p = node_next(p);
445            if (p == page_insert_head) {
446                break;
447            }
448        }
449        return insert_index(p) == i ? insert_total_height(p) : 0;
450    } else {
451        return 0;
452    }
453}
454
455/*tex The |class_insert| zero serves as a garbage bin. */
456
457halfword tex_scan_insert_index(void)
458{
459    halfword index = 0;
460    switch (lmt_insert_state.mode) {
461        case unset_insert_mode:
462            lmt_insert_state.mode = index_insert_mode;
463            // fall-through
464        case index_insert_mode:
465            index = tex_scan_box_register_number();
466            if (index == output_box_par) {
467                tex_handle_error(
468                    normal_error_type,
469                    "You can't \\insert%i",
470                    output_box_par,
471                    "I'm changing to \\insert0; box \\outputbox is special."
472                );
473                index = 0;
474            }
475            break;
476        case class_insert_mode:
477            index = tex_scan_integer(0, NULL, NULL);
478            if (! tex_valid_insert_id(index)) {
479                index = 0;
480            }
481            break;
482    }
483    return index;
484}
485
486void tex_set_insert_mode(halfword mode)
487{
488    if (lmt_insert_state.mode == unset_insert_mode && (mode == index_insert_mode || mode == class_insert_mode)) {
489        lmt_insert_state.mode = mode;
490    } else if (mode != lmt_insert_state.mode) {
491        tex_handle_error(
492            normal_error_type,
493            "Bad \\insertmode (%i)",
494            mode,
495            "This mode can be set once and has value 1 or 2. It will be automatically\n"
496            "set when \\insert is used."
497        );
498    }
499}
500
501int tex_insert_is_void(halfword i)
502{
503    halfword b = tex_aux_insert_box(i);
504    return (! b) || box_list(b) == null; /*tex So also an empty box test! */
505}
506
507/* playground */
508
509int tex_insert_stored(void)
510{
511    return lmt_insert_state.head != null;
512}
513
514void tex_insert_restore(halfword n)
515{
516    if (lmt_insert_state.tail) {
517        tex_couple_nodes(lmt_insert_state.tail, n);
518    } else {
519        lmt_insert_state.head = n;
520    }
521    lmt_insert_state.tail = n;
522}
523
524void tex_insert_store(halfword i, halfword n)
525{
526    if (tex_get_insert_storage(i)) {
527        tex_insert_restore(n);
528    }
529}
530
531/* not sparse (yet) ... makes no sense (unless we make the list pointers) */
532
533void tex_dump_insert_data(dumpstream f) {
534    dump_int(f, lmt_insert_state.mode);
535    dump_int(f, lmt_insert_state.insert_data.ptr);
536    dump_int(f, lmt_insert_state.insert_data.top);
537    dump_things(f, lmt_insert_state.inserts[0], lmt_insert_state.insert_data.ptr);
538}
539
540void tex_undump_insert_data(dumpstream f) {
541    insert_record *tmp;
542    undump_int(f, lmt_insert_state.mode);
543    undump_int(f, lmt_insert_state.insert_data.ptr);
544    undump_int(f, lmt_insert_state.insert_data.top);
545    tmp = aux_allocate_clear_array(sizeof(insert_record), lmt_insert_state.insert_data.top, 1);
546    if (tmp) {
547        lmt_insert_state.inserts = tmp;
548        lmt_insert_state.insert_data.allocated = lmt_insert_state.insert_data.top;
549        undump_things(f, lmt_insert_state.inserts[0], lmt_insert_state.insert_data.ptr);
550    } else {
551        tex_overflow_error("inserts", lmt_insert_state.insert_data.top);
552    }
553}
554
555/*tex
556    Inserts, not the easiest mechanism and a candicate for more opening up.
557*/
558
559void tex_run_insert(void)
560{
561    int brace = 0;
562    halfword callback = 0;
563    halfword data = 0;
564    halfword index = -1;
565    while (1) {
566        switch (tex_scan_character("cdiCDI", 1, 1, 1)) {
567            case 0:
568                goto DONE;
569            case 'c': case 'C':
570                if (tex_scan_mandate_keyword("callback", 1)) {
571                    callback = tex_scan_integer(0, NULL, NULL);
572                }
573                break;
574            case 'd': case 'D':
575                /* identifier */
576                if (tex_scan_mandate_keyword("data", 1)) {
577                    data = tex_scan_integer(0, NULL, NULL);
578                }
579                break;
580            case 'i': case 'I':
581                if (tex_scan_mandate_keyword("index", 1)) {
582                    index = tex_scan_insert_index();
583                }
584                break;
585            case '{':
586                brace = 1;
587                goto DONE;
588            default:
589                goto DONE;
590        }
591    }
592  DONE:
593    if (index < 0) {
594        index = tex_scan_insert_index();
595    }
596    saved_inserts_initialize();
597    saved_insert_index = index;
598    saved_insert_data = data;
599    saved_insert_callback = callback;
600    lmt_save_state.save_stack_data.ptr += saved_insert_n_of_records;
601    tex_new_save_level(insert_group);
602    if (! brace) {
603        tex_scan_left_brace();
604    }
605    tex_normal_paragraph(insert_par_context);
606    tex_push_nest();
607    cur_list.mode = internal_vmode;
608    cur_list.prev_depth = ignore_depth_criterion_par;
609}
610
611void tex_finish_insert_group(void)
612{
613    if (! tex_wrapped_up_paragraph(insert_par_context, 0)) {
614        halfword p, q; /*tex for short-term use */
615        scaled d;      /*tex holds |split_max_depth| in |insert_group| */
616        halfword f;    /*tex holds |floating_penalty| in |insert_group| */
617        tex_end_paragraph(insert_group, insert_par_context);
618        q = tex_new_glue_node(split_top_skip_par, top_skip_code);
619        d = split_max_depth_par;
620        f = floating_penalty_par;
621        tex_unsave();
622        lmt_save_state.save_stack_data.ptr -= saved_insert_n_of_records;
623     // p = tex_vpack(node_next(cur_list.head), 0, packing_additional, max_dimension, direction_unknown);
624     // /* we don't do this: */
625     // /* p = tex_filtered_vpack(node_next(cur_list.head), 0, packing_additional, max_dimension, insert_group, direction_unknown, 0, 0); */
626     // /* because it can induce loops. */
627     // tex_pop_nest();
628        p = node_next(cur_list.head);
629        tex_pop_nest();
630        /*tex
631            An |\insert| is just a list. We package it because we want to know the height but then 
632            discard the wrapper |vlist| node. So the |insert_list| is not packaged.
633        */
634        p = tex_vpack(p, 0, packing_additional, max_dimension, direction_unknown, holding_none_option, NULL);
635        {
636            halfword index = saved_insert_index;
637            halfword data = saved_insert_data;
638            halfword callback = saved_insert_callback;
639            halfword insert = tex_new_node(insert_node, 0);
640            halfword maxdepth = tex_get_insert_maxdepth(index);
641            halfword floating = tex_get_insert_penalty(index);
642            if (tex_get_insert_storage(index)) {
643                tex_insert_store(index, insert);
644            } else {
645                tex_tail_append(insert);
646            }
647            insert_index(insert) = index;
648            insert_identifier(insert) = data;
649            insert_callback(insert) = callback;
650            insert_total_height(insert) = box_total(p);
651            insert_list(insert) = box_list(p);
652            insert_split_top(insert) = q;
653            insert_max_depth(insert) = has_insert_option(index, insert_option_maxdepth) ? d : maxdepth;
654            insert_float_cost(insert) = has_insert_option(index, insert_option_penalty) ? f : floating;
655            insert_line_height(insert) = tex_get_insert_line_height(index);
656            insert_line_depth(insert) = tex_get_insert_line_depth(index);
657            insert_stretch(insert) = tex_get_insert_stretch(index);
658            insert_shrink(insert) = tex_get_insert_shrink(index);
659            box_list(p) = null;
660            tex_flush_node(p);
661            if (tracing_inserts_par > 0) {
662                tex_begin_diagnostic();
663                tex_print_levels();
664                tex_print_format("[insert: setting, index %i, height %p, depth %p, penalty %i, topskip %Q]",
665                    index, insert_total_height(insert), insert_max_depth(insert), insert_float_cost(insert), q, pt_unit);
666                if (tracing_inserts_par > 1) {
667                    tex_print_node_list(insert_list(insert), "insert", show_box_depth_par, show_box_breadth_par);
668                }
669                tex_end_diagnostic();
670            }
671        }
672        /* we never do the callback ... maybe move it outside */
673        if (lmt_nest_state.nest_data.ptr == 0) {
674            tex_build_page(insert_page_context, 0);
675        }
676    }
677}
678
679/*tex 
680    This one is meant for the elements (slots) in balanced lists but as we can have them packaged
681    we try to handle this. 
682*/
683
684int tex_identify_inserts(halfword b, halfword cbk)
685{
686    halfword value = 0;
687    while (b) { 
688        switch (node_type(b)) {
689            case hlist_node:
690            case vlist_node:
691                switch (node_subtype(b)) {
692                    case hbox_list:
693                    case container_list:
694                    case unknown_list:
695                        break;
696                    case balance_slot_list:
697                        goto DONE;
698                    default: 
699                        return value;
700                }
701                break;
702            default: 
703                return value; 
704        }
705        b = box_list(b);
706    }
707  DONE:
708    if (b && node_type(b) == vlist_node) {
709        halfword current = box_list(b);
710        while (current) { 
711            if (node_type(current) == insert_node) {
712                int callback = lmt_callback_defined(balance_insert_callback);
713                if (callback) {
714                    ++lmt_balance_state.n_of_callbacks;
715                    lmt_run_callback(lmt_lua_state.lua_instance, callback, "Nddd->",
716                        current, 
717                        cbk,
718                        insert_index(current),
719                        insert_identifier(current)
720                    );
721                }
722                value |= has_inserts;
723                if (insert_list(current)) {
724                    value |= has_inserts_with_content;
725                }
726                if (insert_total_height(current) > 0 && tex_get_insert_multiplier(insert_index(current)) > 0) {
727                    value |= has_inserts_with_height;
728                }
729            }
730            current = node_next(current);
731        }
732    }
733    return value;
734}
735
736scaled tex_insert_height(halfword node)
737{
738    /*tex A redundant check but we do it anyway. */
739    if (node && node_type(node) == insert_node) {
740        halfword multiplier = tex_get_insert_multiplier(insert_index(node));
741        halfword needed = insert_total_height(node);
742        if (multiplier > 0 && needed > 0) {
743            return tex_x_over_n(needed, scaling_factor) * multiplier;
744        }
745    }
746    return 0;
747}
748
749# define set_bit(bits,n) bits[n/8] |= (1 << (index % 8))
750# define get_bit(bits,n) (1 & (bits[index/8] >> (n % 8)))
751
752void tex_insert_reset_distances(void)
753{
754    for (int index = 0; index <= lmt_insert_state.insert_data.top; index++) {
755        if (lmt_insert_state.inserts[index].before) { 
756            tex_flush_node(lmt_insert_state.inserts[index].before);
757        } 
758        lmt_insert_state.inserts[index].before = null;
759        if (lmt_insert_state.inserts[index].inbetween) { 
760            tex_flush_node(lmt_insert_state.inserts[index].inbetween);
761        } 
762        lmt_insert_state.inserts[index].inbetween = null;
763    }
764}
765
766scaled tex_insert_distances(halfword first, halfword last, scaled *stretch, scaled *shrink)
767{
768    char bits[(max_n_of_inserts/8)+1] = { 0 }; /* brrr */
769    int isfirst = 1;
770    scaled amount = 0;
771    halfword c = first; 
772    while (c != last) {
773        if (node_type(c) == insert_node && insert_total_height(c) > 0 && tex_get_insert_multiplier(insert_index(c)) > 0) { 
774            halfword distance = null;
775            halfword index = insert_index(c);
776            if (isfirst) { 
777                if (lmt_insert_state.inserts[index].before) {
778                    distance = lmt_insert_state.inserts[index].before;
779                } else { 
780                    distance = lmt_get_insert_distance(index, 1); /* first */
781                    lmt_insert_state.inserts[index].before = distance;
782                }
783                isfirst = 0;
784                set_bit(bits,index);
785            } else if (insert_index(c) == index && ! get_bit(bits, index)) {
786                if (lmt_insert_state.inserts[index].inbetween) {
787                    distance = lmt_insert_state.inserts[index].inbetween;
788                } else {
789                    distance = lmt_get_insert_distance(index, 2); /* inbetween */
790                    lmt_insert_state.inserts[index].inbetween = distance;
791                }
792                set_bit(bits,index);
793            }
794            if (distance) { 
795                amount += glue_amount(distance);
796                if (stretch) {
797                    *stretch += glue_stretch(distance); /* ignore order, at least we can't handle that now */
798                }
799                if (shrink) {
800                    *shrink += glue_shrink(distance);   /* no order, no infite warning either */
801                }
802            }
803        }
804        c = node_next(c);
805    }
806    return amount;
807}
808