textextcodes.c /size: 23 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    Contrary to traditional \TEX\ we have catcode tables so that we can switch catcode regimes very
10    fast. We can have many such regimes and they're stored in trees.
11
12*/
13
14/*tex
15
16    Using nibbles for catcodes is somewhat more messy but saves 230MB on the format file so it is
17    worth the trouble.
18
19*/
20
21# define nibbled_catcodes 1 
22
23# define CATCODESTACK     8
24# define CATCODESTEP      8
25# define CATCODEDEFAULT  12
26
27# if nibbled_catcodes
28    # define CATCODEDEFAULTS 0xCCCCCCCC /*tex Used as |dflt| value in |sa| struct. */
29# else 
30    # define CATCODEDEFAULTS 0x0C0C0C0C /*tex Used as |dflt| value in |sa| struct. */
31# endif 
32
33typedef struct catcode_state_info {
34    sa_tree       *catcode_heads;
35    unsigned char *catcode_valid;
36    int            catcode_max;
37    int            padding;
38} catcode_state_info;
39
40static catcode_state_info lmt_catcode_state = {
41    .catcode_heads = NULL,
42    .catcode_valid = NULL,
43    .catcode_max   = 0,
44    .padding       = 0,
45} ;
46
47static void tex_aux_allocate_catcodes(void)
48{
49    lmt_catcode_state.catcode_heads = sa_malloc_array(sizeof(sa_tree), max_n_of_catcode_tables);
50    lmt_catcode_state.catcode_valid = sa_malloc_array(sizeof(unsigned char), max_n_of_catcode_tables);
51    if (lmt_catcode_state.catcode_heads && lmt_catcode_state.catcode_valid) {
52        sa_wipe_array(lmt_catcode_state.catcode_heads, sizeof(sa_tree), max_n_of_catcode_tables);
53        sa_wipe_array(lmt_catcode_state.catcode_valid, sizeof(unsigned char), max_n_of_catcode_tables);
54    } else {
55        tex_overflow_error("catcodes", max_n_of_catcode_tables);
56    }
57}
58
59static void tex_aux_initialize_catcodes(void)
60{
61    sa_tree_item item = { .uint_value = CATCODEDEFAULTS };
62    lmt_catcode_state.catcode_max = 0;
63    tex_aux_allocate_catcodes();
64    lmt_catcode_state.catcode_valid[0] = 1;
65# if nibbled_catcodes
66    lmt_catcode_state.catcode_heads[0] = sa_new_tree(catcode_sparse_identifier, CATCODESTACK, CATCODESTEP, 0, item);
67# else 
68    lmt_catcode_state.catcode_heads[0] = sa_new_tree(catcode_sparse_identifier, CATCODESTACK, CATCODESTEP, 1, item);
69# endif 
70}
71
72void tex_set_cat_code(int h, int n, halfword v, int gl)
73{
74    sa_tree_item item = { .uint_value = CATCODEDEFAULTS };
75    sa_tree tree = lmt_catcode_state.catcode_heads[h];
76    if (! tree) {
77        if (h > lmt_catcode_state.catcode_max) {
78            lmt_catcode_state.catcode_max = h;
79        }
80# if nibbled_catcodes
81        tree = sa_new_tree(catcode_sparse_identifier, CATCODESTACK, CATCODESTEP, 0, item);
82# else 
83        tree = sa_new_tree(catcode_sparse_identifier, CATCODESTACK, CATCODESTEP,1, item);
84# endif 
85        lmt_catcode_state.catcode_heads[h] = tree;
86    }
87# if nibbled_catcodes
88    sa_set_item_0(tree, n, v, gl);
89# else 
90    sa_set_item_1(tree, n, v, gl);
91# endif 
92}
93
94halfword tex_get_cat_code(int h, int n)
95{
96    sa_tree_item item = { .uint_value = CATCODEDEFAULTS };
97    sa_tree tree = lmt_catcode_state.catcode_heads[h];
98    if (! tree) {
99        if (h > lmt_catcode_state.catcode_max) {
100            lmt_catcode_state.catcode_max = h;
101        }
102# if nibbled_catcodes
103        tree = sa_new_tree(catcode_sparse_identifier, CATCODESTACK, CATCODESTEP, 0, item);
104# else 
105        tree = sa_new_tree(catcode_sparse_identifier, CATCODESTACK, CATCODESTEP, 1, item);
106# endif 
107        lmt_catcode_state.catcode_heads[h] = tree;
108    }
109# if nibbled_catcodes
110    return sa_return_item_0(tree, n);
111# else 
112    return sa_return_item_1(tree, n);
113# endif 
114}
115
116void tex_unsave_cat_codes(int h, int gl)
117{
118    if (h > lmt_catcode_state.catcode_max) {
119        lmt_catcode_state.catcode_max = h;
120    }
121    for (int k = 0; k <= lmt_catcode_state.catcode_max; k++) {
122        if (lmt_catcode_state.catcode_heads[k]) {
123            sa_restore_stack(lmt_catcode_state.catcode_heads[k], gl);
124        }
125    }
126}
127
128void tex_restore_cat_codes(int h, int level)
129{
130    if (h > lmt_catcode_state.catcode_max) {
131        lmt_catcode_state.catcode_max = h;
132    }
133    for (int k = 0; k <= lmt_catcode_state.catcode_max; k++) {
134        if (lmt_catcode_state.catcode_heads[k]) {
135            sa_reinit_stack(lmt_catcode_state.catcode_heads[k], level);
136        }
137    }
138}
139
140static void tex_aux_dump_catcodes(dumpstream f)
141{
142    int total = 0;
143    for (int k = 0; k <= lmt_catcode_state.catcode_max; k++) {
144        if (lmt_catcode_state.catcode_valid[k]) {
145            total++;
146        }
147    }
148    dump_int(f, lmt_catcode_state.catcode_max);
149    dump_int(f, total);
150    dump_via_int(f, nibbled_catcodes);
151    for (int k = 0; k <= lmt_catcode_state.catcode_max; k++) {
152        if (lmt_catcode_state.catcode_valid[k]) {
153            dump_int(f, k);
154            sa_dump_tree(f, lmt_catcode_state.catcode_heads[k]);
155        }
156    }
157}
158
159static void tex_aux_undump_catcodes(dumpstream f)
160{
161    int total, nibbled;
162    sa_free_array(lmt_catcode_state.catcode_heads);
163    sa_free_array(lmt_catcode_state.catcode_valid);
164    tex_aux_allocate_catcodes();
165    undump_int(f, lmt_catcode_state.catcode_max);
166    undump_int(f, total);
167    undump_int(f, nibbled);
168    if (nibbled == nibbled_catcodes) { 
169        for (int k = 0; k < total; k++) {
170            int x;
171            undump_int(f, x);
172            lmt_catcode_state.catcode_heads[x] = sa_undump_tree(f);
173            lmt_catcode_state.catcode_valid[x] = 1;
174        }
175    } else { 
176        tex_fatal_undump_error("nibbled catcodes mismatch");
177    }
178}
179
180int tex_valid_catcode_table(int h)
181{
182    return (h >= 0 && h < max_n_of_catcode_tables && lmt_catcode_state.catcode_valid[h]);
183}
184
185void tex_copy_cat_codes(int from, int to)
186{
187    if (from < 0 || from >= max_n_of_catcode_tables || lmt_catcode_state.catcode_valid[from] == 0) {
188        exit(EXIT_FAILURE);
189    } else {
190        if (to > lmt_catcode_state.catcode_max) {
191            lmt_catcode_state.catcode_max = to;
192        }
193        sa_destroy_tree(lmt_catcode_state.catcode_heads[to]);
194        lmt_catcode_state.catcode_heads[to] = sa_copy_tree(lmt_catcode_state.catcode_heads[from]);
195        lmt_catcode_state.catcode_valid[to] = 1;
196    }
197}
198
199/*
200void set_cat_code_table_default(int h, int dflt)
201{
202    if (valid_catcode_table(h)) {
203        catcode_state.catcode_heads[h]->dflt.uchar_value[0] = (unsigned char) dflt;
204        catcode_state.catcode_heads[h]->dflt.uchar_value[1] = (unsigned char) dflt;
205        catcode_state.catcode_heads[h]->dflt.uchar_value[2] = (unsigned char) dflt;
206        catcode_state.catcode_heads[h]->dflt.uchar_value[3] = (unsigned char) dflt;
207    }
208}
209
210int get_cat_code_table_default(int h)
211{
212    if (valid_catcode_table(h)) {
213        return catcode_state.catcode_heads[h]->dflt.uchar_value[0];
214    } else {
215        return CATCODEDEFAULT;
216    }
217}
218*/
219
220void tex_initialize_cat_codes(int h)
221{
222    if (h > lmt_catcode_state.catcode_max) {
223        lmt_catcode_state.catcode_max = h;
224    }
225    sa_destroy_tree(lmt_catcode_state.catcode_heads[h]);
226    lmt_catcode_state.catcode_heads[h] = NULL;
227    tex_set_cat_code(h, '\r', end_line_cmd, 1);
228    tex_set_cat_code(h, ' ', spacer_cmd, 1);
229    tex_set_cat_code(h, '\\', escape_cmd, 1);
230    tex_set_cat_code(h, '%', comment_cmd, 1);
231    tex_set_cat_code(h, 127, invalid_char_cmd, 1);
232    tex_set_cat_code(h, 0, ignore_cmd, 1);
233    tex_set_cat_code(h, 0xFEFF, ignore_cmd, 1);
234    for (int k = 'A'; k <= 'Z'; k++) {
235        tex_set_cat_code(h, k, letter_cmd, 1);
236        tex_set_cat_code(h, k + 'a' - 'A', letter_cmd, 1);
237    }
238    lmt_catcode_state.catcode_valid[h] = 1;
239}
240
241static void tex_aux_free_catcodes(void)
242{
243    for (int k = 0; k <= lmt_catcode_state.catcode_max; k++) {
244        if (lmt_catcode_state.catcode_valid[k]) {
245            sa_destroy_tree(lmt_catcode_state.catcode_heads[k]);
246        }
247    }
248    lmt_catcode_state.catcode_heads = sa_free_array(lmt_catcode_state.catcode_heads);
249    lmt_catcode_state.catcode_valid = sa_free_array(lmt_catcode_state.catcode_valid);
250}
251
252/*tex
253
254    We have a whole bunch of character related codes. We could consider packign them all in one big
255    character blob but this more fits in teh way \TEX\ is designed.  
256
257*/
258
259# define LCCODESTACK      8
260# define LCCODESTEP       8
261# define LCCODEDEFAULT    0
262
263# define UCCODESTACK      8
264# define UCCODESTEP       8
265# define UCCODEDEFAULT    0
266
267# define SFCODESTACK      8
268# define SFCODESTEP       8
269# define SFCODEDEFAULT    default_space_factor
270
271# define HCCODESTACK      8
272# define HCCODESTEP       8
273# define HCCODEDEFAULT    0
274
275# define HMCODESTACK      8
276# define HMCODESTEP       8
277# define HMCODEDEFAULT    0
278
279# define AMCODESTACK      8
280# define AMCODESTEP       8
281# define AMCODEDEFAULT    0
282
283# define CCCODESTACK      8 /* no need for stack */
284# define CCCODESTEP       8 /* no need for stack */
285# define CCCODEDEFAULT    default_character_control
286
287typedef struct luscode_state_info {
288    sa_tree uccode_head;
289    sa_tree lccode_head;
290    sa_tree sfcode_head;
291    sa_tree hccode_head;
292    sa_tree hmcode_head;
293    sa_tree amcode_head;
294    sa_tree cccode_head;
295} luscode_state_info;
296
297static luscode_state_info lmt_luscode_state = {
298    .uccode_head = NULL,
299    .lccode_head = NULL,
300    .sfcode_head = NULL,
301    .hccode_head = NULL,
302    .hmcode_head = NULL,
303    .amcode_head = NULL,
304    .cccode_head = NULL,
305};
306
307/*tex
308
309    The lowercase mapping codes are also stored in a tree. Let's keep them close for cache hits,
310    maybe also with hjcodes.
311
312*/
313
314void tex_set_lc_code(int n, halfword v, int gl)
315{
316    sa_tree_item item = { .int_value = v };
317    sa_set_item_4(lmt_luscode_state.lccode_head, n, item, gl);
318}
319
320halfword tex_get_lc_code(int n)
321{
322    return sa_return_item_4(lmt_luscode_state.lccode_head, n);
323}
324
325static void tex_aux_unsave_lccodes(int gl)
326{
327    sa_restore_stack(lmt_luscode_state.lccode_head, gl);
328}
329
330static void tex_aux_initialize_lccodes(void)
331{
332    sa_tree_item item = {.int_value = LCCODEDEFAULT };
333    lmt_luscode_state.lccode_head = sa_new_tree(lccode_sparse_identifier, LCCODESTACK, LCCODESTEP, 4, item);
334}
335
336static void tex_aux_dump_lccodes(dumpstream f)
337{
338    sa_dump_tree(f, lmt_luscode_state.lccode_head);
339}
340
341static void tex_aux_undump_lccodes(dumpstream f)
342{
343    lmt_luscode_state.lccode_head = sa_undump_tree(f);
344}
345
346static void tex_aux_free_lccodes(void)
347{
348    sa_destroy_tree(lmt_luscode_state.lccode_head);
349}
350
351/*tex
352
353    And the uppercase mapping codes are again stored in a tree.
354
355*/
356
357void tex_set_uc_code(int n, halfword v, int gl)
358{
359    sa_tree_item item = { .int_value = v };
360    sa_set_item_4(lmt_luscode_state.uccode_head, n, item, gl);
361}
362
363halfword tex_get_uc_code(int n)
364{
365    return sa_return_item_4(lmt_luscode_state.uccode_head, n);
366}
367
368static void tex_aux_unsave_uccodes(int gl)
369{
370    sa_restore_stack(lmt_luscode_state.uccode_head, gl);
371}
372
373static void tex_aux_initialize_uccodes(void)
374{
375    sa_tree_item item = { .int_value = UCCODEDEFAULT };
376    lmt_luscode_state.uccode_head = sa_new_tree(uccode_sparse_identifier, UCCODESTACK, UCCODESTEP, 4, item);
377}
378
379static void tex_aux_dump_uccodes(dumpstream f)
380{
381    sa_dump_tree(f,lmt_luscode_state.uccode_head);
382}
383
384static void tex_aux_undump_uccodes(dumpstream f)
385{
386    lmt_luscode_state.uccode_head = sa_undump_tree(f);
387}
388
389static void tex_aux_free_uccodes(void)
390{
391    sa_destroy_tree(lmt_luscode_state.uccode_head);
392}
393
394/*tex
395
396    By now it will be no surprise that the space factors get stored in a tree.
397
398*/
399
400void tex_set_sf_code(int n, halfword v, int gl)
401{
402    sa_tree_item item = { .int_value = v };
403    sa_set_item_4(lmt_luscode_state.sfcode_head, n, item, gl);
404}
405
406halfword tex_get_sf_code(int n)
407{
408    return sa_return_item_4(lmt_luscode_state.sfcode_head, n);
409}
410
411static void tex_aux_unsave_sfcodes(int gl)
412{
413    sa_restore_stack(lmt_luscode_state.sfcode_head, gl);
414}
415
416static void tex_aux_initialize_sfcodes(void)
417{
418    sa_tree_item item = { .int_value = SFCODEDEFAULT };
419    lmt_luscode_state.sfcode_head = sa_new_tree(sfcode_sparse_identifier, SFCODESTACK, SFCODESTEP, 4, item);
420}
421
422static void tex_aux_dump_sfcodes(dumpstream f)
423{
424    sa_dump_tree(f, lmt_luscode_state.sfcode_head);
425}
426
427static void tex_aux_undump_sfcodes(dumpstream f)
428{
429    lmt_luscode_state.sfcode_head = sa_undump_tree(f);
430}
431
432static void tex_aux_free_sfcodes(void)
433{
434    sa_destroy_tree(lmt_luscode_state.sfcode_head);
435}
436
437/*tex
438
439    Finaly the hyphen character codes, a rather small sparse array.
440
441*/
442
443void tex_set_hc_code(int n, halfword v, int gl)
444{
445    sa_tree_item item = { .int_value = v };
446    sa_set_item_4(lmt_luscode_state.hccode_head, n, item, gl);
447}
448
449halfword tex_get_hc_code(int n)
450{
451    return sa_return_item_4(lmt_luscode_state.hccode_head, n);
452}
453
454static void tex_aux_unsave_hccodes(int gl)
455{
456    sa_restore_stack(lmt_luscode_state.hccode_head, gl);
457}
458
459static void tex_aux_initialize_hccodes(void)
460{
461    sa_tree_item item = { .int_value = HCCODEDEFAULT };
462    lmt_luscode_state.hccode_head = sa_new_tree(hccode_sparse_identifier, HCCODESTACK, HCCODESTEP, 4, item);
463}
464
465static void tex_aux_dump_hccodes(dumpstream f)
466{
467    sa_dump_tree(f, lmt_luscode_state.hccode_head);
468}
469
470static void tex_aux_undump_hccodes(dumpstream f)
471{
472    lmt_luscode_state.hccode_head = sa_undump_tree(f);
473}
474
475static void tex_aux_free_hccodes(void)
476{
477    sa_destroy_tree(lmt_luscode_state.hccode_head);
478}
479
480/*tex 
481    The same is true for math hyphenation but here we have a small options set. 
482*/
483
484void tex_set_hm_code(int n, halfword v, int gl)
485{
486    sa_set_item_1(lmt_luscode_state.hmcode_head, n, v, gl);
487}
488
489halfword tex_get_hm_code(int n)
490{
491    return sa_return_item_1(lmt_luscode_state.hmcode_head, n);
492}
493
494static void tex_aux_unsave_hmcodes(int gl)
495{
496    sa_restore_stack(lmt_luscode_state.hmcode_head, gl);
497}
498
499static void tex_aux_initialize_hmcodes(void)
500{
501    sa_tree_item item = { .int_value = HMCODEDEFAULT };
502    lmt_luscode_state.hmcode_head = sa_new_tree(hmcode_sparse_identifier, HMCODESTACK, HMCODESTEP, 1, item);
503}
504
505static void tex_aux_dump_hmcodes(dumpstream f)
506{
507    sa_dump_tree(f, lmt_luscode_state.hmcode_head);
508}
509
510static void tex_aux_undump_hmcodes(dumpstream f)
511{
512    lmt_luscode_state.hmcode_head = sa_undump_tree(f);
513}
514
515static void tex_aux_free_hmcodes(void)
516{
517    sa_destroy_tree(lmt_luscode_state.hmcode_head);
518}
519
520/*tex Experiment. */
521
522
523void tex_set_am_code(int n, halfword v, int gl)
524{
525    sa_set_item_1(lmt_luscode_state.amcode_head, n, v, gl);
526}
527
528halfword tex_get_am_code(int n)
529{
530    return sa_return_item_1(lmt_luscode_state.amcode_head, n);
531}
532
533static void tex_aux_unsave_amcodes(int gl)
534{
535    sa_restore_stack(lmt_luscode_state.amcode_head, gl);
536}
537
538static void tex_aux_initialize_amcodes(void)
539{
540    sa_tree_item item = { .int_value = AMCODEDEFAULT };
541    lmt_luscode_state.amcode_head = sa_new_tree(amcode_sparse_identifier, AMCODESTACK, AMCODESTEP, 1, item);
542}
543
544static void tex_aux_dump_amcodes(dumpstream f)
545{
546    sa_dump_tree(f, lmt_luscode_state.amcode_head);
547}
548
549static void tex_aux_undump_amcodes(dumpstream f)
550{
551    lmt_luscode_state.amcode_head = sa_undump_tree(f);
552}
553
554static void tex_aux_free_amcodes(void)
555{
556    sa_destroy_tree(lmt_luscode_state.amcode_head);
557}
558
559/*tex 
560
561    This is not yet used. 
562
563*/
564
565void tex_set_cc_code(int n, halfword v, int gl)
566{
567    sa_set_item_2(lmt_luscode_state.cccode_head, n, v, gl);
568}
569
570halfword tex_get_cc_code(int n)
571{
572    return sa_return_item_2(lmt_luscode_state.cccode_head, n);
573}
574
575static void tex_aux_unsave_cccodes(int gl)
576{
577    sa_restore_stack(lmt_luscode_state.cccode_head, gl);
578}
579
580static void tex_aux_initialize_cccodes(void)
581{
582    sa_tree_item item = {.int_value = CCCODEDEFAULT };
583    lmt_luscode_state.cccode_head = sa_new_tree(lccode_sparse_identifier, CCCODESTACK, CCCODESTEP, 2, item);
584}
585
586static void tex_aux_dump_cccodes(dumpstream f)
587{
588    sa_dump_tree(f, lmt_luscode_state.cccode_head);
589}
590
591static void tex_aux_undump_cccodes(dumpstream f)
592{
593    lmt_luscode_state.cccode_head = sa_undump_tree(f);
594}
595
596static void tex_aux_free_cccodes(void)
597{
598    sa_destroy_tree(lmt_luscode_state.cccode_head);
599}
600
601/*tex
602
603    The hyphenation codes are indeed stored in a tree and are used instead of lowercase codes when
604    deciding what characters to take into acccount when hyphenating. They are bound to upto
605    |HJCODE_MAX| languages. In the end I decided to put the hash pointer in the language record
606    so that we can do better lean memory management. Actually, the hjcode handling already was more
607    efficient than in \LUATEX\ because I kept track of usage and allocated (dumped) only the
608    languages that were used. A typical example of nicely cleaned up code that in the end was
609    ditched but that happens often (and of course goes unnoticed). Actually, in \CONTEXT\ we don't
610    dump language info at all, so I might as wel drop language dumping, just like fonts.
611
612*/
613
614# define HJCODESTACK   8
615# define HJCODESTEP    8
616# define HJCODEDEFAULT 0
617
618void tex_set_hj_code(int h, int n, halfword v, int gl)
619{
620    if (h >= 0 && h <= lmt_language_state.language_data.top) {
621        sa_tree_item item = { .int_value = HJCODEDEFAULT };
622        sa_tree tree = lmt_language_state.languages[h]->hjcode_head;
623        if (! tree) {
624            tree = sa_new_tree(hjcode_sparse_identifier, HJCODESTACK, HJCODESTEP, 4, item);
625            lmt_language_state.languages[h]->hjcode_head = tree;
626        }
627        if (tree) {
628            item.int_value = (int) v;
629            sa_set_item_4(tree, n, item, gl);
630        }
631    }
632}
633
634/*tex We just return the lccodes when nothing is set. */
635
636halfword tex_get_hj_code(int h, int n)
637{
638    if (h >= 0 && h <= lmt_language_state.language_data.top) {
639        sa_tree tree = lmt_language_state.languages[h]->hjcode_head;
640        if (! tree) {
641            tree = lmt_luscode_state.lccode_head;
642        }
643        return sa_return_item_4(tree, n);
644    } else {
645        return 0;
646    }
647}
648
649void tex_dump_language_hj_codes(dumpstream f, int h)
650{
651    if (h >= 0 && h <= lmt_language_state.language_data.top) {
652        sa_tree tree = lmt_language_state.languages[h]->hjcode_head;
653        if (tree) {
654            dump_via_uchar(f, 1);
655            sa_dump_tree(f, tree);
656        } else {
657            dump_via_uchar(f, 0);
658        }
659    } else {
660       /* error */
661    }
662}
663
664void tex_undump_language_hj_codes(dumpstream f, int h)
665{
666    if (h >= 0 && h <= lmt_language_state.language_data.top) {
667        unsigned char marker;
668        undump_uchar(f, marker);
669        if (marker) {
670            sa_free_array(lmt_language_state.languages[h]->hjcode_head);
671            lmt_language_state.languages[h]->hjcode_head = sa_undump_tree(f);
672        } else {
673            lmt_language_state.languages[h]->hjcode_head = NULL;
674        }
675    } else {
676       /* error */
677    }
678}
679
680void tex_hj_codes_from_lc_codes(int h)
681{
682    if (h >= 0 && h <= lmt_language_state.language_data.top) {
683        sa_tree tree = lmt_language_state.languages[h]->hjcode_head;
684        if (tree) {
685            sa_destroy_tree(tree);
686        }
687        tree = sa_copy_tree(lmt_luscode_state.lccode_head);
688        lmt_language_state.languages[h]->hjcode_head = tree ? tree : NULL;
689    }
690}
691
692/*tex The public management functions. */
693
694void tex_unsave_text_codes(int grouplevel)
695{
696    tex_aux_unsave_lccodes(grouplevel);
697    tex_aux_unsave_uccodes(grouplevel);
698    tex_aux_unsave_sfcodes(grouplevel);
699    tex_aux_unsave_hccodes(grouplevel);
700    tex_aux_unsave_hmcodes(grouplevel);
701    tex_aux_unsave_amcodes(grouplevel);
702    tex_aux_unsave_cccodes(grouplevel);
703}
704
705void tex_initialize_text_codes(void)
706{
707    tex_aux_initialize_catcodes();
708    tex_aux_initialize_lccodes();
709    tex_aux_initialize_uccodes();
710    tex_aux_initialize_sfcodes();
711    tex_aux_initialize_hccodes();
712    tex_aux_initialize_hmcodes();
713    tex_aux_initialize_amcodes();
714    tex_aux_initialize_cccodes();
715 /* initializehjcodes(); */
716}
717
718void tex_free_text_codes(void)
719{
720    tex_aux_free_catcodes();
721    tex_aux_free_lccodes();
722    tex_aux_free_uccodes();
723    tex_aux_free_sfcodes();
724    tex_aux_free_hccodes();
725    tex_aux_free_hmcodes();
726    tex_aux_free_amcodes();
727    tex_aux_free_cccodes();
728 /* freehjcodes(); */
729}
730
731void tex_dump_text_codes(dumpstream f)
732{
733    tex_aux_dump_catcodes(f);
734    tex_aux_dump_lccodes(f);
735    tex_aux_dump_uccodes(f);
736    tex_aux_dump_sfcodes(f);
737    tex_aux_dump_hccodes(f);
738    tex_aux_dump_hmcodes(f);
739    tex_aux_dump_amcodes(f);
740    tex_aux_dump_cccodes(f);
741 /* dumphjcodes(f); */
742}
743
744void tex_undump_text_codes(dumpstream f)
745{
746    tex_aux_undump_catcodes(f);
747    tex_aux_undump_lccodes(f);
748    tex_aux_undump_uccodes(f);
749    tex_aux_undump_sfcodes(f);
750    tex_aux_undump_hccodes(f);
751    tex_aux_undump_hmcodes(f);
752    tex_aux_undump_amcodes(f);
753    tex_aux_undump_cccodes(f);
754 /* undumphjcodes(f); */
755}
756
757void tex_initialize_xx_codes(void)
758{
759    /*tex We're compatible. */
760    for (int u = 'A'; u <= 'Z'; u++) {
761        int l = u + 32;
762        tex_set_lc_code(u, l, level_one);
763        tex_set_lc_code(l, l, level_one);
764        tex_set_uc_code(u, u, level_one);
765        tex_set_uc_code(l, u, level_one);
766        tex_set_sf_code(u, special_space_factor, level_one);
767    }
768    /*tex A good start but not compatible. */
769 /* set_hc_code(0x002D, 0x002D, level_one); */
770 /* set_hc_code(0x2010, 0x2010, level_one); */
771}
772
773void tex_run_case_shift(halfword code)
774{
775    int upper = code == upper_case_code;
776    halfword l = tex_scan_toks_normal(0, NULL);
777    halfword p = token_link(l);
778    while (p) {
779        halfword t = token_info(p);
780        if (t < cs_token_flag) {
781            halfword c = t % cs_offset_value;
782            halfword i = upper ? tex_get_uc_code(c) : tex_get_lc_code(c);
783            if (i) {
784                set_token_info(p, t - c + i);
785            }
786        } else if (tex_is_active_cs(cs_text(t - cs_token_flag))) {
787            halfword c = active_cs_value(cs_text(t - cs_token_flag));
788            halfword i = upper ? tex_get_uc_code(c) : tex_get_lc_code(c);
789            if (i) {
790                set_token_info(p, tex_active_to_cs(i, 1) + cs_token_flag);
791            }
792        }
793        p = token_link(p);
794    }
795    if (token_link(l)) {
796        tex_begin_backed_up_list(token_link(l));
797    }
798    tex_put_available_token(l);
799}
800
801/*tex
802    Maybe some day: |sparse_identifier|, |fontchar_identifier|, |mathfont_identifier| and/or 
803    |mathparam_identifier|.
804*/
805
806void tex_show_code_stack()
807{
808    sa_tree head = NULL;
809    tex_get_token();
810    switch (cur_cmd) { 
811        case define_char_code_cmd:
812            switch (cur_chr) {
813                case amcode_charcode: 
814                    head = lmt_luscode_state.amcode_head; 
815                    break;
816                case cccode_charcode: 
817                    head = lmt_luscode_state.cccode_head; 
818                    break;
819                case catcode_charcode: 
820                    if (cat_code_table_par >= 0 && cat_code_table_par < max_n_of_catcode_tables) {
821                        head = lmt_catcode_state.catcode_heads[cat_code_table_par];
822                    }
823                    break;   
824                case delcode_charcode: 
825                case extdelcode_charcode: 
826                    /* maybe */
827                    break;
828                case hccode_charcode: 
829                    head = lmt_luscode_state.hccode_head; 
830                    break;    
831                case hmcode_charcode: 
832                    head = lmt_luscode_state.hmcode_head; 
833                    break;    
834                case lccode_charcode: 
835                    head = lmt_luscode_state.lccode_head; 
836                    break;    
837                case mathcode_charcode:
838                case extmathcode_charcode: 
839                    /* maybe */
840                    break;
841                case sfcode_charcode     : 
842                    head = lmt_luscode_state.sfcode_head; 
843                    break;    
844                case uccode_charcode     : 
845                    head = lmt_luscode_state.uccode_head; 
846                    break;    
847            }
848            break;
849        case internal_integer_cmd: 
850            switch (cur_chr) {
851                case cat_code_table_code:
852                    if (cat_code_table_par >= 0 && cat_code_table_par < max_n_of_catcode_tables) {
853                        head = lmt_catcode_state.catcode_heads[cat_code_table_par];
854                    }
855            }
856            break;
857        case hyphenation_cmd:
858            switch (cur_chr) {
859                case hjcode_code: 
860                    if (language_par >= 0 && language_par <= lmt_language_state.language_data.top) {
861                        head = lmt_language_state.languages[language_par]->hjcode_head;
862                    }
863                    break;
864            }
865            break;
866    }
867    if (head) {
868        sa_show_stack(head);
869    }
870}
871
872