texmarks.c /size: 11 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7/*tex
8
9    A control sequence that has been |\def|'ed by the user is expanded by \TEX's |macro_call|
10    procedure.
11
12    Before we get into the details of |macro_call|, however, let's consider the treatment of
13    primitives like |\topmark|, since they are essentially macros without parameters. The token
14    lists for such marks are kept in five global arrays of pointers; we refer to the individual
15    entries of these arrays by symbolic macros |top_mark|, etc. The value of |top_mark (x)|, etc.
16    is either |null| or a pointer to the reference count of a token list.
17
18    The variable |biggest_used_mark| is an aid to try and keep the code somehwat efficient without
19    too much extra work: it registers the highest mark class ever instantiated by the user, so the
20    loops in |fire_up| and |vsplit| do not have to traverse the full range |0 .. biggest_mark|.
21
22    Watch out: zero is always valid and the good old single mark!
23
24*/
25
26mark_state_info lmt_mark_state = {
27    .data      = NULL,
28    .min_used  = -1,
29    .max_used  = -1,
30    .mark_data = {
31        .minimum   = min_mark_size,
32        .maximum   = max_mark_size,
33        .size      = memory_data_unset,
34        .step      = stp_mark_size,
35        .allocated = 0,
36        .itemsize  = sizeof(mark_record),
37        .top       = 0,
38        .ptr       = 0,
39        .initial   = memory_data_unset,
40        .offset    = 0,
41    },
42};
43
44void tex_initialize_marks(void)
45{
46    /* allocated: minimum + 1 */
47    lmt_mark_state.data = aux_allocate_clear_array(sizeof(mark_record), lmt_mark_state.mark_data.minimum, 1);
48    if (lmt_mark_state.data) {
49        lmt_mark_state.mark_data.allocated = sizeof(mark_record) * lmt_mark_state.mark_data.minimum;
50        lmt_mark_state.mark_data.top = lmt_mark_state.mark_data.minimum;
51    }
52}
53
54void tex_reset_mark(halfword m)
55{
56    if (m >= lmt_mark_state.mark_data.top) {
57       int step = lmt_mark_state.mark_data.step;
58       int size = lmt_mark_state.mark_data.top;
59       /* regular stepwise bump */
60       while (m >= size) {
61           size += step;
62       }
63       /* last resort */
64       if (size > lmt_mark_state.mark_data.maximum) {
65           size = m;
66       }
67       if (size <= lmt_mark_state.mark_data.maximum) {
68           mark_record *tmp = aux_reallocate_array(lmt_mark_state.data, sizeof(mark_record), size, 1);
69           if (tmp) {
70               lmt_mark_state.data = tmp;
71               memset(&lmt_mark_state.data[lmt_mark_state.mark_data.top], 0, sizeof(mark_record) * (size - lmt_mark_state.mark_data.top));
72               lmt_mark_state.mark_data.top = size;
73               lmt_mark_state.mark_data.allocated = sizeof(mark_record) * size;
74           } else {
75               tex_overflow_error("marks", size);
76           }
77        } else {
78            tex_overflow_error("marks", lmt_mark_state.mark_data.maximum);
79       }
80    }
81    if (m > lmt_mark_state.mark_data.ptr) {
82        lmt_mark_state.mark_data.ptr = m;
83    }
84    tex_wipe_mark(m);
85}
86
87halfword tex_get_mark(halfword m, halfword s)
88{
89    if (s >= 0 && s <= last_unique_mark_code) {
90        return lmt_mark_state.data[m][s];
91    } else {
92        return null;
93    }
94}
95
96void tex_set_mark(halfword m, halfword s, halfword v)
97{
98    if (s >= 0 && s <= last_unique_mark_code) {
99        if (lmt_mark_state.data[m][s]) {
100            tex_delete_token_reference(lmt_mark_state.data[m][s]);
101        }
102        if (v) {
103            tex_add_token_reference(v);
104        }
105        lmt_mark_state.data[m][s] = v;
106    }
107}
108
109int tex_valid_mark(halfword m) {
110    if (m >= lmt_mark_state.mark_data.top) {
111        tex_reset_mark(m);
112    }
113    return m < lmt_mark_state.mark_data.top;
114}
115
116halfword tex_new_mark(quarterword subtype, halfword index, halfword ptr)
117{
118    halfword mark = tex_new_node(mark_node, subtype);
119    mark_index(mark) = index;
120    mark_ptr(mark) = ptr;
121    if (lmt_mark_state.min_used < 0) {
122        lmt_mark_state.min_used = index;
123        lmt_mark_state.max_used = index;
124    } else {
125        if (index < lmt_mark_state.min_used) {
126            lmt_mark_state.min_used = index;
127        }
128        if (index > lmt_mark_state.max_used) {
129            lmt_mark_state.max_used = index;
130        }
131    }
132    tex_set_mark(index, current_marks_code, ptr);
133    return mark;
134}
135
136static void tex_aux_print_mark(const char *s, halfword t)
137{
138    if (t) {
139        tex_print_token_list(s, token_link(t));
140    }
141}
142
143void tex_show_marks()
144{
145    if (tracing_marks_par > 0 && lmt_mark_state.min_used >= 0) {
146        tex_begin_diagnostic();
147        for (halfword m = lmt_mark_state.min_used; m <= lmt_mark_state.max_used; m++) {
148            if (tex_has_mark(m)) {
149                tex_print_format("[mark: class %i, page state]",m);
150                tex_aux_print_mark("top",         tex_get_mark(m, top_marks_code));
151                tex_aux_print_mark("first",       tex_get_mark(m, first_marks_code));
152                tex_aux_print_mark("bot",         tex_get_mark(m, bot_marks_code));
153                tex_aux_print_mark("split first", tex_get_mark(m, split_first_marks_code));
154                tex_aux_print_mark("split bot",   tex_get_mark(m, split_bot_marks_code));
155                tex_aux_print_mark("current",     tex_get_mark(m, current_marks_code));
156            }
157        }
158        tex_end_diagnostic();
159    }
160}
161
162void tex_update_top_marks()
163{
164    if (lmt_mark_state.min_used >= 0) {
165        for (halfword m = lmt_mark_state.min_used; m <= lmt_mark_state.max_used; m++) {
166            halfword bot = tex_get_mark(m, bot_marks_code);
167            if (bot) {
168                tex_set_mark(m, top_marks_code, bot);
169                if (tracing_marks_par > 1) {
170                    tex_begin_diagnostic();
171                    tex_print_format("[mark: class %i, top becomes bot]", m);
172                    tex_aux_print_mark(NULL, bot);
173                    tex_end_diagnostic();
174                }
175                tex_delete_mark(m, first_marks_code);
176            }
177        }
178    }
179}
180
181void tex_update_first_and_bot_mark(halfword n)
182{
183    halfword index = mark_index(n);
184    halfword ptr = mark_ptr(n);
185    if (node_subtype(n) == reset_mark_value_code) {
186        /*tex Work in progress. */
187        if (tracing_marks_par > 1) {
188            tex_begin_diagnostic();
189            tex_print_format("[mark: index %i, reset]", index);
190            tex_end_diagnostic();
191        }
192        tex_reset_mark(index);
193    } else {
194        /*tex Update the values of |first_mark| and |bot_mark|. */
195        halfword first = tex_get_mark(index, first_marks_code);
196        if (! first) {
197            tex_set_mark(index, first_marks_code, ptr);
198            if (tracing_marks_par > 1) {
199                tex_begin_diagnostic();
200                tex_print_format("[mark: index %i, first becomes mark]", index);
201                tex_aux_print_mark(NULL, ptr);
202                tex_end_diagnostic();
203            }
204        }
205        tex_set_mark(index, bot_marks_code, ptr);
206        if (tracing_marks_par > 1) {
207            tex_begin_diagnostic();
208            tex_print_format("[mark: index %i, bot becomes mark]", index);
209            tex_aux_print_mark(NULL, ptr);
210            tex_end_diagnostic();
211        }
212    }
213}
214
215void tex_update_first_marks(void)
216{
217    if (lmt_mark_state.min_used >= 0) {
218        for (halfword m = lmt_mark_state.min_used; m <= lmt_mark_state.max_used; m++) {
219            halfword top = tex_get_mark(m, top_marks_code);
220            halfword first = tex_get_mark(m, first_marks_code);
221            if (top && ! first) {
222                tex_set_mark(m, first_marks_code, top);
223                if (tracing_marks_par > 1) {
224                    tex_begin_diagnostic();
225                    tex_print_format("[mark: class %i, first becomes top]", m);
226                    tex_aux_print_mark(NULL, top);
227                    tex_end_diagnostic();
228                }
229            }
230        }
231    }
232}
233
234void tex_update_split_mark(halfword n)
235{
236    halfword index = mark_index(n);
237    halfword ptr = mark_ptr(n);
238    if (node_subtype(n) == reset_mark_value_code) {
239        tex_reset_mark(index);
240    } else {
241        if (tex_get_mark(index, split_first_marks_code)) {
242            tex_set_mark(index, split_bot_marks_code, ptr);
243            if (tracing_marks_par > 1) {
244                tex_begin_diagnostic();
245                tex_print_format("[mark: index %i, split bot becomes mark]", index);
246                tex_aux_print_mark(NULL, tex_get_mark(index, split_bot_marks_code));
247                tex_end_diagnostic();
248            }
249        } else {
250            tex_set_mark(index, split_first_marks_code, ptr);
251            tex_set_mark(index, split_bot_marks_code, ptr);
252            if (tracing_marks_par > 1) {
253                tex_begin_diagnostic();
254                tex_print_format("[mark: index %i, split first becomes mark]", index);
255                tex_aux_print_mark(NULL, tex_get_mark(index, split_first_marks_code));
256                tex_print_format("[mark: index %i, split bot becomes split first]", index);
257                tex_aux_print_mark(NULL, tex_get_mark(index, split_bot_marks_code));
258                tex_end_diagnostic();
259            }
260        }
261    }
262}
263
264
265void tex_delete_mark(halfword m, int what)
266{
267    switch (what) {
268        case top_mark_code        : what = top_marks_code;
269        case first_mark_code      : what = first_marks_code;
270        case bot_mark_code        : what = bot_marks_code;
271        case split_first_mark_code: what = split_first_marks_code;
272        case split_bot_mark_code  : what = split_bot_marks_code;
273    }
274    tex_set_mark(m, what, null);
275}
276
277halfword tex_get_some_mark(halfword chr, halfword val)
278{
279    switch (chr) {
280        case top_mark_code        : val = top_marks_code;
281        case first_mark_code      : val = first_marks_code;
282        case bot_mark_code        : val = bot_marks_code;
283        case split_first_mark_code: val = split_first_marks_code;
284        case split_bot_mark_code  : val = split_bot_marks_code;
285    }
286    return tex_get_mark(val, chr);
287}
288
289void tex_wipe_mark(halfword m)
290{
291    for (int what = 0; what <= last_unique_mark_code; what++) {
292        tex_set_mark(m, what, null);
293    }
294}
295
296int tex_has_mark(halfword m)
297{
298    for (int what = 0; what <= last_unique_mark_code; what++) {
299        if (lmt_mark_state.data[m][what]) {
300            return 1;
301        }
302    }
303    return 0;
304}
305
306/*tex
307
308    The |make_mark| procedure has been renamed, because if the current chr code is 1, then the
309    actual command was |\clearmarks|, which did not generate a mark node but instead destroyed the
310    current mark related tokenlists. We now have proper reset nodes.
311
312*/
313
314void tex_run_mark(void)
315{
316    halfword index = 0;
317    halfword code = cur_chr;
318    switch (code) {
319        case set_marks_code:
320        case clear_marks_code:
321        case flush_marks_code:
322            index = tex_scan_mark_number();
323            break;
324    }
325    if (tex_valid_mark(index)) {
326        quarterword subtype = set_mark_value_code;
327        halfword ptr = null;
328        switch (code) {
329            case set_marks_code:
330            case set_mark_code:
331                ptr = tex_scan_toks_expand(0, NULL, 0, 0);
332                break;
333            case clear_marks_code:
334                tex_wipe_mark(index);
335                return;
336            case flush_marks_code:
337                subtype = reset_mark_value_code;
338                break;
339        }
340        tex_tail_append(tex_new_mark(subtype, index, ptr));
341    } else {
342        /* error already issued */
343    }
344}
345