texmarks.c /size: 12 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    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        .extra     = 0, 
42    },
43};
44
45void tex_initialize_marks(void)
46{
47    /* allocated: minimum + 1 */
48    lmt_mark_state.data = aux_allocate_clear_array(sizeof(mark_record), lmt_mark_state.mark_data.minimum, 1);
49    if (lmt_mark_state.data) {
50        lmt_mark_state.mark_data.allocated = lmt_mark_state.mark_data.minimum;
51        lmt_mark_state.mark_data.top = lmt_mark_state.mark_data.minimum;
52    }
53}
54
55void tex_reset_mark(halfword m)
56{
57    if (m >= lmt_mark_state.mark_data.top) {
58       int step = lmt_mark_state.mark_data.step;
59       int size = lmt_mark_state.mark_data.top;
60       /* regular stepwise bump */
61       while (m >= size) {
62           size += step;
63       }
64       /* last resort */
65       if (size > lmt_mark_state.mark_data.maximum) {
66           size = m;
67       }
68       if (size <= lmt_mark_state.mark_data.maximum) {
69           mark_record *tmp = aux_reallocate_array(lmt_mark_state.data, sizeof(mark_record), size, 1);
70           if (tmp) {
71               lmt_mark_state.data = tmp;
72               memset(&lmt_mark_state.data[lmt_mark_state.mark_data.top], 0, sizeof(mark_record) * (size - lmt_mark_state.mark_data.top));
73               lmt_mark_state.mark_data.top = size;
74               lmt_mark_state.mark_data.allocated = size;
75           } else {
76               tex_overflow_error("marks", size);
77           }
78        } else {
79            tex_overflow_error("marks", lmt_mark_state.mark_data.maximum);
80       }
81    }
82    if (m > lmt_mark_state.mark_data.ptr) {
83        lmt_mark_state.mark_data.ptr = m;
84    }
85    tex_wipe_mark(m);
86}
87
88halfword tex_get_mark(halfword m, halfword s)
89{
90    if (s >= 0 && s <= last_unique_mark_code) {
91        return lmt_mark_state.data[m].marks[s];
92    } else {
93        return null;
94    }
95}
96
97void tex_set_mark(halfword m, halfword s, halfword v)
98{
99    if (s >= 0 && s <= last_unique_mark_code) {
100        if (lmt_mark_state.data[m].marks[s]) {
101            tex_delete_token_reference(lmt_mark_state.data[m].marks[s]);
102        }
103        if (v) {
104            tex_add_token_reference(v);
105        }
106        lmt_mark_state.data[m].marks[s] = v;
107        lmt_mark_state.data[m].state = 1;
108    }
109}
110
111int tex_valid_mark(halfword m) {
112    if (m >= lmt_mark_state.mark_data.top) {
113        tex_reset_mark(m);
114    }
115    return m < lmt_mark_state.mark_data.top;
116}
117
118halfword tex_new_mark(quarterword subtype, halfword index, halfword ptr)
119{
120    halfword mark = tex_new_node(mark_node, subtype);
121    mark_index(mark) = index;
122    mark_ptr(mark) = ptr;
123    if (lmt_mark_state.min_used < 0) {
124        lmt_mark_state.min_used = index;
125        lmt_mark_state.max_used = index;
126    } else {
127        if (index < lmt_mark_state.min_used) {
128            lmt_mark_state.min_used = index;
129        }
130        if (index > lmt_mark_state.max_used) {
131            lmt_mark_state.max_used = index;
132        }
133    }
134    tex_set_mark(index, current_marks_code, ptr);
135    return mark;
136}
137
138static void tex_aux_print_mark(const char *s, halfword t)
139{
140    if (t) {
141        /* todo: proper indentation */
142        tex_print_token_list(s, token_link(t));
143    }
144}
145
146void tex_show_marks()
147{
148    if (tracing_marks_par > 0 && lmt_mark_state.min_used >= 0) {
149        tex_begin_diagnostic();
150        for (halfword m = lmt_mark_state.min_used; m <= lmt_mark_state.max_used; m++) {
151            if (tex_has_mark(m)) {
152                tex_print_format("[mark: class %i, page state]",m);
153                tex_aux_print_mark("top",         tex_get_mark(m, top_marks_code));
154                tex_aux_print_mark("first",       tex_get_mark(m, first_marks_code));
155                tex_aux_print_mark("bot",         tex_get_mark(m, bot_marks_code));
156                tex_aux_print_mark("split first", tex_get_mark(m, split_first_marks_code));
157                tex_aux_print_mark("split bot",   tex_get_mark(m, split_bot_marks_code));
158                tex_aux_print_mark("current",     tex_get_mark(m, current_marks_code));
159            }
160        }
161        tex_end_diagnostic();
162    }
163}
164
165int tex_update_top_marks()
166{
167    int done = 0;
168    if (lmt_mark_state.min_used >= 0) {
169        for (halfword m = lmt_mark_state.min_used; m <= lmt_mark_state.max_used; m++) {
170            halfword bot = tex_get_mark(m, bot_marks_code);
171            if (bot) {
172                tex_set_mark(m, top_marks_code, bot);
173                if (tracing_marks_par > 1) {
174                    tex_begin_diagnostic();
175                    tex_print_format("[mark: class %i, top becomes bot]", m);
176                    tex_aux_print_mark(NULL, bot);
177                    tex_end_diagnostic();
178                }
179                tex_delete_mark(m, first_marks_code);
180                done = 1;
181            }
182        }
183    }
184    return done; 
185}
186
187void tex_update_first_and_bot_mark(halfword n)
188{
189    halfword index = mark_index(n);
190    halfword ptr = mark_ptr(n);
191    if (node_subtype(n) == reset_mark_value_code) {
192        /*tex Work in progress. */
193        if (tracing_marks_par > 1) {
194            tex_begin_diagnostic();
195            tex_print_format("[mark: index %i, reset]", index);
196            tex_end_diagnostic();
197        }
198        tex_reset_mark(index);
199    } else {
200        /*tex Update the values of |first_mark| and |bot_mark|. */
201        halfword first = tex_get_mark(index, first_marks_code);
202        if (! first) {
203            tex_set_mark(index, first_marks_code, ptr);
204            if (tracing_marks_par > 1) {
205                tex_begin_diagnostic();
206                tex_print_format("[mark: index %i, first becomes mark]", index);
207                tex_aux_print_mark(NULL, ptr);
208                tex_end_diagnostic();
209            }
210        }
211        tex_set_mark(index, bot_marks_code, ptr);
212        if (tracing_marks_par > 1) {
213            tex_begin_diagnostic();
214            tex_print_format("[mark: index %i, bot becomes mark]", index);
215            tex_aux_print_mark(NULL, ptr);
216            tex_end_diagnostic();
217        }
218    }
219}
220
221int tex_update_first_marks(void)
222{
223    int done = 0;
224    if (lmt_mark_state.min_used >= 0) {
225        for (halfword m = lmt_mark_state.min_used; m <= lmt_mark_state.max_used; m++) {
226            halfword top = tex_get_mark(m, top_marks_code);
227            halfword first = tex_get_mark(m, first_marks_code);
228            if (top && ! first) {
229                tex_set_mark(m, first_marks_code, top);
230                if (tracing_marks_par > 1) {
231                    tex_begin_diagnostic();
232                    tex_print_format("[mark: class %i, first becomes top]", m);
233                    tex_aux_print_mark(NULL, top);
234                    tex_end_diagnostic();
235                }
236                done = 1;
237            }
238        }
239    }
240    return done;
241}
242
243int tex_update_marks(
244    halfword n
245)
246{
247    int done = 0;
248    if (n && node_type(n) == vlist_node) {
249        halfword list = box_list(n);
250        while (list) {
251            if (node_type(list) == mark_node) {
252                tex_update_first_and_bot_mark(list);
253                done = 1;
254            }
255            list = node_next(list);
256        }
257    }
258    return done;
259}
260
261void tex_update_split_mark(halfword n)
262{
263    halfword index = mark_index(n);
264    halfword ptr = mark_ptr(n);
265    if (node_subtype(n) == reset_mark_value_code) {
266        tex_reset_mark(index);
267    } else if (tex_get_mark(index, split_first_marks_code)) {
268        tex_set_mark(index, split_bot_marks_code, ptr);
269        if (tracing_marks_par > 1) {
270            tex_begin_diagnostic();
271            tex_print_format("[mark: index %i, split bot becomes mark]", index);
272            tex_aux_print_mark(NULL, tex_get_mark(index, split_bot_marks_code));
273            tex_end_diagnostic();
274        }
275    } else {
276        tex_set_mark(index, split_first_marks_code, ptr);
277        tex_set_mark(index, split_bot_marks_code, ptr);
278        if (tracing_marks_par > 1) {
279            tex_begin_diagnostic();
280            tex_print_format("[mark: index %i, split first becomes mark]", index);
281            tex_aux_print_mark(NULL, tex_get_mark(index, split_first_marks_code));
282            tex_print_format("[mark: index %i, split bot becomes split first]", index);
283            tex_aux_print_mark(NULL, tex_get_mark(index, split_bot_marks_code));
284            tex_end_diagnostic();
285        }
286    }
287}
288
289void tex_delete_mark(halfword m, int what)
290{
291    switch (what) {
292        case top_mark_code        : what = top_marks_code;
293        case first_mark_code      : what = first_marks_code;
294        case bot_mark_code        : what = bot_marks_code;
295        case split_first_mark_code: what = split_first_marks_code;
296        case split_bot_mark_code  : what = split_bot_marks_code;
297    }
298    tex_set_mark(m, what, null);
299}
300
301halfword tex_get_some_mark(halfword chr, halfword val)
302{
303    switch (chr) {
304        case top_mark_code        : val = top_marks_code;
305        case first_mark_code      : val = first_marks_code;
306        case bot_mark_code        : val = bot_marks_code;
307        case split_first_mark_code: val = split_first_marks_code;
308        case split_bot_mark_code  : val = split_bot_marks_code;
309    }
310    return tex_get_mark(val, chr);
311}
312
313void tex_wipe_mark(halfword m)
314{
315    for (int what = 0; what <= last_unique_mark_code; what++) {
316        tex_set_mark(m, what, null);
317    }
318    lmt_mark_state.data[m].state = 0;
319}
320
321int tex_has_mark(halfword m)
322{
323    if (lmt_mark_state.data[m].state) { 
324        for (int what = 0; what <= last_unique_mark_code; what++) {
325            if (lmt_mark_state.data[m].marks[what]) {
326                return 1;
327            }
328        }
329    }
330    return 0;
331}
332
333/*tex
334
335    The |make_mark| procedure has been renamed, because if the current chr code is 1, then the
336    actual command was |\clearmarks|, which did not generate a mark node but instead destroyed the
337    current mark related tokenlists. We now have proper reset nodes.
338
339*/
340
341void tex_run_mark(void)
342{
343    halfword index = 0;
344    halfword code = cur_chr;
345    switch (code) {
346        case set_marks_code:
347        case clear_marks_code:
348        case flush_marks_code:
349            index = tex_scan_mark_number();
350            break;
351    }
352    if (tex_valid_mark(index)) {
353        quarterword subtype = set_mark_value_code;
354        halfword ptr = null;
355        switch (code) {
356            case set_marks_code:
357            case set_mark_code:
358                ptr = tex_scan_toks_expand(0, NULL, 0, 0);
359                break;
360            case clear_marks_code:
361                tex_wipe_mark(index);
362                return;
363            case flush_marks_code:
364                subtype = reset_mark_value_code;
365                break;
366        }
367        tex_tail_append(tex_new_mark(subtype, index, ptr));
368    } else {
369        /* error already issued */
370    }
371}
372
373extern halfword lmt_get_mark_class(lua_State *L, int index)
374{
375    switch (lua_type(L, index)) {
376        case LUA_TSTRING:
377            {
378                const char *s = lua_tostring(L, index);
379                if (lua_key_eq(s, top)) {
380                    return top_marks_code;
381                } else if (lua_key_eq(s, first)) {
382                    return first_marks_code;
383                } else if (lua_key_eq(s, bottom)) {
384                    return bot_marks_code;
385                } else if (lua_key_eq(s, splitfirst)) {
386                    return split_first_marks_code;
387                } else if (lua_key_eq(s, splitbottom)) {
388                    return split_bot_marks_code;
389                } else if (lua_key_eq(s, current)) {
390                    return current_marks_code;
391                } else {
392                    return -1;
393                }
394            }
395        case LUA_TNUMBER:
396            {
397                halfword m = lmt_tohalfword(L, index);
398                if (m >= 0 && m <= last_unique_mark_code) {
399                    return m; 
400                } else {
401                    return -1;
402                }
403            }
404        default:
405            return -1;
406    }
407}
408