1% mlist.w
2%
3% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org>
4%
5% This file is part of LuaTeX.
6%
7% LuaTeX is free software; you can redistribute it and/or modify it under
8% the terms of the GNU General Public License as published by the Free
9% Software Foundation; either version 2 of the License, or (at your
10% option) any later version.
11%
12% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14% FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15% License for more details.
16%
17% You should have received a copy of the GNU General Public License along
18% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
19
20/* hh-ls: by using couple_nodes instead of vlink we make sure that we can backtrack as well as have valid prev links */
21
22
23\def\LuaTeX{Lua\TeX}
24
25@ @c
26
27
28#include "ptexlib.h"
29#include "lua/luatex-api.h"
30
31@ @c
32#define delimiter_factor     int_par(delimiter_factor_code)
33#define delimiter_shortfall  dimen_par(delimiter_shortfall_code)
34#define bin_op_penalty       int_par(bin_op_penalty_code)
35#define rel_penalty          int_par(rel_penalty_code)
36#define null_delimiter_space dimen_par(null_delimiter_space_code)
37#define script_space         dimen_par(script_space_code)
38#define disable_lig          int_par(disable_lig_code)
39#define disable_kern         int_par(disable_kern_code)
40
41#define nDEBUG
42
43#define reset_attributes(p,newatt) do {             \
44    delete_attribute_ref(node_attr(p));             \
45    node_attr(p) = newatt;                          \
46    if (newatt!=null) {                             \
47      assert(type(newatt)==attribute_list_node);    \
48      add_node_attr_ref(node_attr(p));              \
49    }                                               \
50  } while (0)
51
52
53#define DEFINE_MATH_PARAMETERS(A,B,C,D) do {                            \
54    if (B==text_size) {                                                 \
55      def_math_param(A, text_style, (C),D);                             \
56      def_math_param(A, cramped_text_style, (C),D);                     \
57    } else if (B==script_size) {                                        \
58      def_math_param(A, script_style, (C),D);                           \
59      def_math_param(A, cramped_script_style, (C),D);                   \
60    } else if (B==script_script_size) {                                 \
61      def_math_param(A, script_script_style, (C),D);                    \
62      def_math_param(A, cramped_script_script_style, (C),D);            \
63    }                                                                   \
64  } while (0)
65
66#define DEFINE_DMATH_PARAMETERS(A,B,C,D) do {                           \
67    if (B==text_size) {                                                 \
68      def_math_param(A, display_style,(C),D);                           \
69      def_math_param(A, cramped_display_style,(C),D);                   \
70    }                                                                   \
71  } while (0)
72
73
74#define is_new_mathfont(A) (font_math_params(A)>0)
75#define is_old_mathfont(A,B) (font_math_params(A)==0 && font_params(A)>=(B))
76
77
78#define font_MATH_par(a,b)                                                  \
79  (font_math_params(a)>=b ? font_math_param(a,b) : undefined_math_parameter)
80
81
82@ here are the math parameters that are font-dependant
83
84@ Before an mlist is converted to an hlist, \TeX\ makes sure that
85the fonts in family~2 have enough parameters to be math-symbol
86fonts, and that the fonts in family~3 have enough parameters to be
87math-extension fonts. The math-symbol parameters are referred to by using the
88following macros, which take a size code as their parameter; for example,
89|num1(cur_size)| gives the value of the |num1| parameter for the current size.
90@^parameters for symbols@>
91@^font parameters@>
92
93@c
94#define total_mathsy_params 22
95#define total_mathex_params 13
96
97#define mathsy(A,B) font_param(fam_fnt(2,A),B)
98#define math_x_height(A) mathsy(A,5)    /* height of `\.x' */
99#define math_quad(A) mathsy(A,6)        /* \.{18mu} */
100#define num1(A) mathsy(A,8)     /* numerator shift-up in display styles */
101#define num2(A) mathsy(A,9)     /* numerator shift-up in non-display, non-\.{\\atop} */
102#define num3(A) mathsy(A,10)    /* numerator shift-up in non-display \.{\\atop} */
103#define denom1(A) mathsy(A,11)  /* denominator shift-down in display styles */
104#define denom2(A) mathsy(A,12)  /* denominator shift-down in non-display styles */
105#define sup1(A) mathsy(A,13)    /* superscript shift-up in uncramped display style */
106#define sup2(A) mathsy(A,14)    /* superscript shift-up in uncramped non-display */
107#define sup3(A) mathsy(A,15)    /* superscript shift-up in cramped styles */
108#define sub1(A) mathsy(A,16)    /* subscript shift-down if superscript is absent */
109#define sub2(A) mathsy(A,17)    /* subscript shift-down if superscript is present */
110#define sup_drop(A) mathsy(A,18)        /* superscript baseline below top of large box */
111#define sub_drop(A) mathsy(A,19)        /* subscript baseline below bottom of large box */
112#define delim1(A) mathsy(A,20)  /* size of \.{\\atopwithdelims} delimiters in display styles */
113#define delim2(A) mathsy(A,21)  /* size of \.{\\atopwithdelims} delimiters in non-displays */
114#define axis_height(A) mathsy(A,22)     /* height of fraction lines above the baseline */
115
116
117@ The math-extension parameters have similar macros, but the size code is
118omitted (since it is always |cur_size| when we refer to such parameters).
119@^parameters for symbols@>
120@^font parameters@>
121
122@c
123#define mathex(A,B) font_param(fam_fnt(3,A),B)
124#define default_rule_thickness(A) mathex(A,8)   /* thickness of \.{\\over} bars */
125#define big_op_spacing1(A) mathex(A,9)  /* minimum clearance above a displayed op */
126#define big_op_spacing2(A) mathex(A,10) /* minimum clearance below a displayed op */
127#define big_op_spacing3(A) mathex(A,11) /* minimum baselineskip above displayed op */
128#define big_op_spacing4(A) mathex(A,12) /* minimum baselineskip below displayed op */
129#define big_op_spacing5(A) mathex(A,13) /* padding above and below displayed limits */
130
131@ I (TH) made a bunch of extensions cf. the MATH table in OpenType, but some of
132the MathConstants values have no matching usage in \LuaTeX\ right now.
133
134ScriptPercentScaleDown,
135ScriptScriptPercentScaleDown:
136  These should be handled by the macro package, on the engine
137  side there are three separate fonts
138
139DelimitedSubFormulaMinHeight:
140  This is perhaps related to word's natural math input? I have
141  no idea what to do about it
142
143MathLeading:
144  LuaTeX does not currently handle multi-line displays, and
145  the parameter does not seem to make much sense elsewhere
146
147FlattenedAccentBaseHeight:
148  This is based on the 'flac' GSUB feature. It would not be hard
149  to support that, but proper math accent placements cf. MATH
150  needs support for MathTopAccentAttachment table to be
151  implemented first
152
153SkewedFractionHorizontalGap,
154SkewedFractionVerticalGap:
155  I am not sure it makes sense implementing skewed fractions,
156  so I would like to see an example first
157
158Also still TODO for OpenType Math:
159  * extensible large operators
160  * prescripts
161
162@ this is not really a math parameter at all
163
164@c
165static void math_param_error(const char *param, int style)
166{
167    char s[256];
168    const char *hlp[] = {
169        "Sorry, but I can't typeset math unless various parameters have",
170        "been set. This is normally done by loading special math fonts",
171        "into the math family slots. Your font set is lacking at least",
172        "the parameter mentioned earlier.",
173        NULL
174    };
175    snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set",
176             param, math_style_names[style]);
177    tex_error(s, hlp);
178#if 0
179    flush_math();
180#endif
181    return;
182}
183
184
185@ @c
186static scaled accent_base_height(int f)
187{
188    scaled a;
189    if (is_new_mathfont(f)) {
190        a = font_MATH_par(f, AccentBaseHeight);
191        if (a == undefined_math_parameter)
192            a = x_height(f);
193    } else {
194        a = x_height(f);
195    }
196    return a;
197}
198
199@ the non-staticness of this function is for the benefit of |texmath.w|
200
201@c
202scaled get_math_quad(int var)
203{
204    scaled a = get_math_param(math_param_quad, var);
205    if (a == undefined_math_parameter) {
206        math_param_error("quad", var);
207        a = 0;
208    }
209    return a;
210}
211
212@ this parameter is different because it is called with a size
213   specifier instead of a style specifier.
214
215@c
216static scaled math_axis(int b)
217{
218    scaled a;
219    int var;
220    if (b == script_size)
221        var = script_style;
222    else if (b == script_script_size)
223        var = script_script_style;
224    else
225        var = text_style;
226    a = get_math_param(math_param_axis, var);
227    if (a == undefined_math_parameter) {
228        math_param_error("axis", var);
229        a = 0;
230    }
231    return a;
232}
233
234@ @c
235static scaled get_math_quad_size(int b)
236{
237    int var;
238    if (b == script_size)
239        var = script_style;
240    else if (b == script_script_size)
241        var = script_script_style;
242    else
243        var = text_style;
244    return get_math_param(math_param_quad, var);
245}
246
247
248@ @c
249static scaled minimum_operator_size(int var)
250{
251    scaled a = get_math_param(math_param_operator_size, var);
252    return a;
253}
254
255@ Old-style fonts do not define the |radical_rule|. This allows |make_radical| to select
256 the backward compatibility code, and it means that we can't raise an error here.
257
258@c
259static scaled radical_rule(int var)
260{
261    scaled a = get_math_param(math_param_radical_rule, var);
262    return a;
263}
264
265@ now follow all the trivial math parameters
266
267@c
268#define get_math_param_or_error(a,b) do_get_math_param_or_error(a, math_param_##b, #b)
269
270static scaled do_get_math_param_or_error(int var, int param, const char *name)
271{
272    scaled a = get_math_param(param, var);
273    if (a == undefined_math_parameter) {
274        math_param_error(name, var);
275        a = 0;
276    }
277    return a;
278}
279
280@ @c
281#define radical_degree_before(a) get_math_param_or_error(a, radical_degree_before)
282#define radical_degree_after(a)  get_math_param_or_error(a, radical_degree_after)
283#define radical_degree_raise(a)  get_math_param_or_error(a, radical_degree_raise)
284
285#define connector_overlap_min(a) get_math_param_or_error(a, connector_overlap_min)
286
287#define overbar_rule(a)          get_math_param_or_error(a, overbar_rule)
288#define overbar_kern(a)          get_math_param_or_error(a, overbar_kern)
289#define overbar_vgap(a)          get_math_param_or_error(a, overbar_vgap)
290
291#define underbar_rule(a)         get_math_param_or_error(a, underbar_rule)
292#define underbar_kern(a)         get_math_param_or_error(a, underbar_kern)
293#define underbar_vgap(a)         get_math_param_or_error(a, underbar_vgap)
294
295#define under_delimiter_vgap(a)  get_math_param_or_error(a, under_delimiter_vgap)
296#define under_delimiter_bgap(a)  get_math_param_or_error(a, under_delimiter_bgap)
297
298#define over_delimiter_vgap(a)   get_math_param_or_error(a, over_delimiter_vgap)
299#define over_delimiter_bgap(a)   get_math_param_or_error(a, over_delimiter_bgap)
300
301#define radical_vgap(a)          get_math_param_or_error(a, radical_vgap)
302#define radical_kern(a)          get_math_param_or_error(a, radical_kern)
303
304#define stack_vgap(a)            get_math_param_or_error(a, stack_vgap)
305#define stack_num_up(a)          get_math_param_or_error(a, stack_num_up)
306#define stack_denom_down(a)      get_math_param_or_error(a, stack_denom_down)
307
308#define fraction_rule(a)         get_math_param_or_error(a, fraction_rule)
309#define fraction_num_vgap(a)     get_math_param_or_error(a, fraction_num_vgap)
310#define fraction_denom_vgap(a)   get_math_param_or_error(a, fraction_denom_vgap)
311#define fraction_num_up(a)       get_math_param_or_error(a, fraction_num_up)
312#define fraction_denom_down(a)   get_math_param_or_error(a, fraction_denom_down)
313#define fraction_del_size(a)     get_math_param_or_error(a, fraction_del_size)
314
315#define limit_above_vgap(a)      get_math_param_or_error(a, limit_above_vgap)
316#define limit_above_bgap(a)      get_math_param_or_error(a, limit_above_bgap)
317#define limit_above_kern(a)      get_math_param_or_error(a, limit_above_kern)
318
319#define limit_below_vgap(a)      get_math_param_or_error(a, limit_below_vgap)
320#define limit_below_bgap(a)      get_math_param_or_error(a, limit_below_bgap)
321#define limit_below_kern(a)      get_math_param_or_error(a, limit_below_kern)
322
323#define sub_shift_drop(a)        get_math_param_or_error(a, sub_shift_drop)
324#define sup_shift_drop(a)        get_math_param_or_error(a, sup_shift_drop)
325#define sub_shift_down(a)        get_math_param_or_error(a, sub_shift_down)
326#define sub_sup_shift_down(a)    get_math_param_or_error(a, sub_sup_shift_down)
327#define sup_shift_up(a)          get_math_param_or_error(a, sup_shift_up)
328#define sub_top_max(a)           get_math_param_or_error(a, sub_top_max)
329#define sup_bottom_min(a)        get_math_param_or_error(a, sup_bottom_min)
330#define sup_sub_bottom_max(a)    get_math_param_or_error(a, sup_sub_bottom_max)
331#define subsup_vgap(a)           get_math_param_or_error(a, subsup_vgap)
332
333#define space_after_script(a)    get_math_param_or_error(a, space_after_script)
334
335@ @c
336void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
337{
338    if (is_new_mathfont(f)) {   /* fix all known parameters */
339
340        DEFINE_MATH_PARAMETERS(math_param_quad, size_id, font_size(f), lvl);
341        DEFINE_DMATH_PARAMETERS(math_param_quad, size_id, font_size(f), lvl);
342        DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
343                               font_MATH_par(f, AxisHeight), lvl);
344        DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
345                                font_MATH_par(f, AxisHeight), lvl);
346        DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
347                               font_MATH_par(f, OverbarExtraAscender), lvl);
348        DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
349                                font_MATH_par(f, OverbarExtraAscender), lvl);
350        DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
351                               font_MATH_par(f, OverbarRuleThickness), lvl);
352        DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
353                                font_MATH_par(f, OverbarRuleThickness), lvl);
354        DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
355                               font_MATH_par(f, OverbarVerticalGap), lvl);
356        DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
357                                font_MATH_par(f, OverbarVerticalGap), lvl);
358        DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
359                               font_MATH_par(f, UnderbarExtraDescender), lvl);
360        DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
361                                font_MATH_par(f, UnderbarExtraDescender), lvl);
362        DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
363                               font_MATH_par(f, UnderbarRuleThickness), lvl);
364        DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
365                                font_MATH_par(f, UnderbarRuleThickness), lvl);
366        DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
367                               font_MATH_par(f, UnderbarVerticalGap), lvl);
368        DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
369                                font_MATH_par(f, UnderbarVerticalGap), lvl);
370
371        DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
372                               font_MATH_par(f, StretchStackGapAboveMin), lvl);
373        DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
374                                font_MATH_par(f, StretchStackGapAboveMin), lvl);
375        DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
376                               font_MATH_par(f, StretchStackBottomShiftDown),
377                               lvl);
378        DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
379                                font_MATH_par(f, StretchStackBottomShiftDown),
380                                lvl);
381
382        DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
383                               font_MATH_par(f, StretchStackGapBelowMin), lvl);
384        DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
385                                font_MATH_par(f, StretchStackGapBelowMin), lvl);
386        DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
387                               font_MATH_par(f, StretchStackTopShiftUp), lvl);
388        DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
389                                font_MATH_par(f, StretchStackTopShiftUp), lvl);
390
391
392        DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
393                               font_MATH_par(f, StackTopShiftUp), lvl);
394        DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
395                                font_MATH_par(f, StackTopDisplayStyleShiftUp),
396                                lvl);
397        DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
398                               font_MATH_par(f, StackBottomShiftDown), lvl);
399        DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
400                                font_MATH_par(f,
401                                              StackBottomDisplayStyleShiftDown),
402                                lvl);
403        DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
404                               font_MATH_par(f, StackGapMin), lvl);
405        DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
406                                font_MATH_par(f, StackDisplayStyleGapMin), lvl);
407
408        DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
409                               font_MATH_par(f, RadicalExtraAscender), lvl);
410        DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
411                                font_MATH_par(f, RadicalExtraAscender), lvl);
412
413        DEFINE_DMATH_PARAMETERS(math_param_operator_size, size_id,
414                                font_MATH_par(f, DisplayOperatorMinHeight),
415                                lvl);
416
417        DEFINE_MATH_PARAMETERS(math_param_radical_rule, size_id,
418                               font_MATH_par(f, RadicalRuleThickness), lvl);
419        DEFINE_DMATH_PARAMETERS(math_param_radical_rule, size_id,
420                                font_MATH_par(f, RadicalRuleThickness), lvl);
421        DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
422                               font_MATH_par(f, RadicalVerticalGap), lvl);
423        DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
424                                font_MATH_par(f,
425                                              RadicalDisplayStyleVerticalGap),
426                                lvl);
427        DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
428                               font_MATH_par(f, RadicalKernBeforeDegree), lvl);
429        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
430                                font_MATH_par(f, RadicalKernBeforeDegree), lvl);
431        DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
432                               font_MATH_par(f, RadicalKernAfterDegree), lvl);
433        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
434                                font_MATH_par(f, RadicalKernAfterDegree), lvl);
435        DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
436                               font_MATH_par(f,
437                                             RadicalDegreeBottomRaisePercent),
438                               lvl);
439        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
440                                font_MATH_par(f,
441                                              RadicalDegreeBottomRaisePercent),
442                                lvl);
443        if (size_id == text_size) {
444            def_math_param(math_param_sup_shift_up, display_style,
445                           font_MATH_par(f, SuperscriptShiftUp), lvl);
446            def_math_param(math_param_sup_shift_up, cramped_display_style,
447                           font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
448            def_math_param(math_param_sup_shift_up, text_style,
449                           font_MATH_par(f, SuperscriptShiftUp), lvl);
450            def_math_param(math_param_sup_shift_up, cramped_text_style,
451                           font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
452        } else if (size_id == script_size) {
453            def_math_param(math_param_sup_shift_up, script_style,
454                           font_MATH_par(f, SuperscriptShiftUp), lvl);
455            def_math_param(math_param_sup_shift_up, cramped_script_style,
456                           font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
457        } else if (size_id == script_script_size) {
458            def_math_param(math_param_sup_shift_up, script_script_style,
459                           font_MATH_par(f, SuperscriptShiftUp), lvl);
460            def_math_param(math_param_sup_shift_up, cramped_script_script_style,
461                           font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
462        }
463
464        DEFINE_MATH_PARAMETERS(math_param_sub_shift_drop, size_id,
465                               font_MATH_par(f, SubscriptBaselineDropMin), lvl);
466        DEFINE_DMATH_PARAMETERS(math_param_sub_shift_drop, size_id,
467                                font_MATH_par(f, SubscriptBaselineDropMin),
468                                lvl);
469        DEFINE_MATH_PARAMETERS(math_param_sup_shift_drop, size_id,
470                               font_MATH_par(f, SuperscriptBaselineDropMax),
471                               lvl);
472        DEFINE_DMATH_PARAMETERS(math_param_sup_shift_drop, size_id,
473                                font_MATH_par(f, SuperscriptBaselineDropMax),
474                                lvl);
475        DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
476                               font_MATH_par(f, SubscriptShiftDown), lvl);
477        DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
478                                font_MATH_par(f, SubscriptShiftDown), lvl);
479
480        if (font_MATH_par(f, SubscriptShiftDownWithSuperscript) !=
481            undefined_math_parameter) {
482            DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
483                                   font_MATH_par(f,
484                                                 SubscriptShiftDownWithSuperscript),
485                                   lvl);
486            DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
487                                    font_MATH_par(f,
488                                                  SubscriptShiftDownWithSuperscript),
489                                    lvl);
490        } else {
491            DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
492                                   font_MATH_par(f, SubscriptShiftDown), lvl);
493            DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
494                                    font_MATH_par(f, SubscriptShiftDown), lvl);
495        }
496
497        DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
498                               font_MATH_par(f, SubscriptTopMax), lvl);
499        DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
500                                font_MATH_par(f, SubscriptTopMax), lvl);
501        DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
502                               font_MATH_par(f, SuperscriptBottomMin), lvl);
503        DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
504                                font_MATH_par(f, SuperscriptBottomMin), lvl);
505        DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
506                               font_MATH_par(f,
507                                             SuperscriptBottomMaxWithSubscript),
508                               lvl);
509        DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
510                                font_MATH_par(f,
511                                              SuperscriptBottomMaxWithSubscript),
512                                lvl);
513        DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
514                               font_MATH_par(f, SubSuperscriptGapMin), lvl);
515        DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
516                                font_MATH_par(f, SubSuperscriptGapMin), lvl);
517
518        DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
519                               font_MATH_par(f, UpperLimitGapMin), lvl);
520        DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
521                                font_MATH_par(f, UpperLimitGapMin), lvl);
522        DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
523                               font_MATH_par(f, UpperLimitBaselineRiseMin),
524                               lvl);
525        DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
526                                font_MATH_par(f, UpperLimitBaselineRiseMin),
527                                lvl);
528        DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id, 0, lvl);
529        DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id, 0, lvl);
530        DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
531                               font_MATH_par(f, LowerLimitGapMin), lvl);
532        DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
533                                font_MATH_par(f, LowerLimitGapMin), lvl);
534        DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
535                               font_MATH_par(f, LowerLimitBaselineDropMin),
536                               lvl);
537        DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
538                                font_MATH_par(f, LowerLimitBaselineDropMin),
539                                lvl);
540        DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id, 0, lvl);
541        DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id, 0, lvl);
542
543        DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
544                               font_MATH_par(f, FractionRuleThickness), lvl);
545        DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
546                                font_MATH_par(f, FractionRuleThickness), lvl);
547        DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
548                               font_MATH_par(f, FractionNumeratorGapMin), lvl);
549        DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
550                                font_MATH_par(f,
551                                              FractionNumeratorDisplayStyleGapMin),
552                                lvl);
553        DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
554                               font_MATH_par(f, FractionNumeratorShiftUp), lvl);
555        DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
556                                font_MATH_par(f,
557                                              FractionNumeratorDisplayStyleShiftUp),
558                                lvl);
559        DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
560                               font_MATH_par(f, FractionDenominatorGapMin),
561                               lvl);
562        DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
563                                font_MATH_par(f,
564                                              FractionDenominatorDisplayStyleGapMin),
565                                lvl);
566        DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
567                               font_MATH_par(f, FractionDenominatorShiftDown),
568                               lvl);
569        DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
570                                font_MATH_par(f,
571                                              FractionDenominatorDisplayStyleShiftDown),
572                                lvl);
573
574        DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
575                               font_MATH_par(f, FractionDelimiterSize), lvl);
576        DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
577                                font_MATH_par(f,
578                                              FractionDelimiterDisplayStyleSize),
579                                lvl);
580
581        DEFINE_MATH_PARAMETERS(math_param_space_after_script, size_id,
582                               font_MATH_par(f, SpaceAfterScript), lvl);
583        DEFINE_DMATH_PARAMETERS(math_param_space_after_script, size_id,
584                                font_MATH_par(f, SpaceAfterScript), lvl);
585
586        DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
587                               font_MATH_par(f, MinConnectorOverlap), lvl);
588        DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
589                                font_MATH_par(f, MinConnectorOverlap), lvl);
590
591
592    } else if (fam_id == 2 && is_old_mathfont(f, total_mathsy_params)) {
593        /* fix old-style |sy| parameters */
594        DEFINE_MATH_PARAMETERS(math_param_quad, size_id, math_quad(size_id),
595                               lvl);
596        DEFINE_DMATH_PARAMETERS(math_param_quad, size_id, math_quad(size_id),
597                                lvl);
598        DEFINE_MATH_PARAMETERS(math_param_axis, size_id, axis_height(size_id),
599                               lvl);
600        DEFINE_DMATH_PARAMETERS(math_param_axis, size_id, axis_height(size_id),
601                                lvl);
602        DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id, num3(size_id),
603                               lvl);
604        DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id, num1(size_id),
605                                lvl);
606        DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
607                               denom2(size_id), lvl);
608        DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
609                                denom1(size_id), lvl);
610        DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
611                               num2(size_id), lvl);
612        DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
613                                num1(size_id), lvl);
614        DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
615                               denom2(size_id), lvl);
616        DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
617                                denom1(size_id), lvl);
618        DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
619                               delim2(size_id), lvl);
620        DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
621                                delim1(size_id), lvl);
622        if (size_id == text_size) {
623            def_math_param(math_param_sup_shift_up, display_style,
624                           sup1(size_id), lvl);
625            def_math_param(math_param_sup_shift_up, cramped_display_style,
626                           sup3(size_id), lvl);
627            def_math_param(math_param_sup_shift_up, text_style, sup2(size_id),
628                           lvl);
629            def_math_param(math_param_sup_shift_up, cramped_text_style,
630                           sup3(size_id), lvl);
631        } else if (size_id == script_size) {
632            def_math_param(math_param_sub_shift_drop, display_style,
633                           sub_drop(size_id), lvl);
634            def_math_param(math_param_sub_shift_drop, cramped_display_style,
635                           sub_drop(size_id), lvl);
636            def_math_param(math_param_sub_shift_drop, text_style,
637                           sub_drop(size_id), lvl);
638            def_math_param(math_param_sub_shift_drop, cramped_text_style,
639                           sub_drop(size_id), lvl);
640            def_math_param(math_param_sup_shift_drop, display_style,
641                           sup_drop(size_id), lvl);
642            def_math_param(math_param_sup_shift_drop, cramped_display_style,
643                           sup_drop(size_id), lvl);
644            def_math_param(math_param_sup_shift_drop, text_style,
645                           sup_drop(size_id), lvl);
646            def_math_param(math_param_sup_shift_drop, cramped_text_style,
647                           sup_drop(size_id), lvl);
648            def_math_param(math_param_sup_shift_up, script_style, sup2(size_id),
649                           lvl);
650            def_math_param(math_param_sup_shift_up, cramped_script_style,
651                           sup3(size_id), lvl);
652        } else if (size_id == script_script_size) {
653            def_math_param(math_param_sub_shift_drop, script_style,
654                           sub_drop(size_id), lvl);
655            def_math_param(math_param_sub_shift_drop, cramped_script_style,
656                           sub_drop(size_id), lvl);
657            def_math_param(math_param_sub_shift_drop, script_script_style,
658                           sub_drop(size_id), lvl);
659            def_math_param(math_param_sub_shift_drop,
660                           cramped_script_script_style, sub_drop(size_id), lvl);
661            def_math_param(math_param_sup_shift_drop, script_style,
662                           sup_drop(size_id), lvl);
663            def_math_param(math_param_sup_shift_drop, cramped_script_style,
664                           sup_drop(size_id), lvl);
665            def_math_param(math_param_sup_shift_drop, script_script_style,
666                           sup_drop(size_id), lvl);
667            def_math_param(math_param_sup_shift_drop,
668                           cramped_script_script_style, sup_drop(size_id), lvl);
669            def_math_param(math_param_sup_shift_up, script_script_style,
670                           sup2(size_id), lvl);
671            def_math_param(math_param_sup_shift_up, cramped_script_script_style,
672                           sup3(size_id), lvl);
673        }
674        DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
675                               sub1(size_id), lvl);
676        DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
677                                sub1(size_id), lvl);
678        DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
679                               sub2(size_id), lvl);
680        DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
681                                sub2(size_id), lvl);
682        DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
683                               (abs(math_x_height(size_id) * 4) / 5), lvl);
684        DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
685                                (abs(math_x_height(size_id) * 4) / 5), lvl);
686        DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
687                               (abs(math_x_height(size_id)) / 4), lvl);
688        DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
689                                (abs(math_x_height(size_id)) / 4), lvl);
690        DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
691                               (abs(math_x_height(size_id) * 4) / 5), lvl);
692        DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
693                                (abs(math_x_height(size_id) * 4) / 5), lvl);
694
695        /* The display-size |radical_vgap| is done twice because it needs
696           values from both the sy and the ex font. */
697        DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
698                                (default_rule_thickness(size_id) +
699                                 (abs(math_x_height(size_id)) / 4)), lvl);
700
701        DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
702                               60, lvl);
703        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
704                                60, lvl);
705        DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
706                               xn_over_d(get_math_quad_size(size_id), 5, 18),
707                               lvl);
708        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
709                                xn_over_d(get_math_quad_size(size_id), 5, 18),
710                                lvl);
711        DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
712                               (-xn_over_d
713                                (get_math_quad_size(size_id), 10, 18)), lvl);
714        DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
715                                (-xn_over_d
716                                 (get_math_quad_size(size_id), 10, 18)), lvl);
717
718    } else if (fam_id == 3 && is_old_mathfont(f, total_mathex_params)) {
719        /* fix old-style |ex| parameters */
720        DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
721                               default_rule_thickness(size_id), lvl);
722        DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
723                               default_rule_thickness(size_id), lvl);
724        DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
725                               3 * default_rule_thickness(size_id), lvl);
726        DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
727                                default_rule_thickness(size_id), lvl);
728        DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
729                                default_rule_thickness(size_id), lvl);
730        DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
731                                3 * default_rule_thickness(size_id), lvl);
732        DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
733                               default_rule_thickness(size_id), lvl);
734        DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
735                               default_rule_thickness(size_id), lvl);
736        DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
737                               3 * default_rule_thickness(size_id), lvl);
738        DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
739                                default_rule_thickness(size_id), lvl);
740        DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
741                                default_rule_thickness(size_id), lvl);
742        DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
743                                3 * default_rule_thickness(size_id), lvl);
744        DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
745                               default_rule_thickness(size_id), lvl);
746        DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
747                                default_rule_thickness(size_id), lvl);
748
749        DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
750                               (default_rule_thickness(size_id) +
751                                (abs(default_rule_thickness(size_id)) / 4)),
752                               lvl);
753
754        DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
755                               3 * default_rule_thickness(size_id), lvl);
756        DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
757                                7 * default_rule_thickness(size_id), lvl);
758        DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
759                               default_rule_thickness(size_id), lvl);
760        DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
761                                default_rule_thickness(size_id), lvl);
762        DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
763                               default_rule_thickness(size_id), lvl);
764        DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
765                                3 * default_rule_thickness(size_id), lvl);
766        DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
767                               default_rule_thickness(size_id), lvl);
768        DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
769                                3 * default_rule_thickness(size_id), lvl);
770        DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
771                               big_op_spacing1(size_id), lvl);
772        DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
773                                big_op_spacing1(size_id), lvl);
774        DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
775                               big_op_spacing3(size_id), lvl);
776        DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
777                                big_op_spacing3(size_id), lvl);
778        DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
779                               big_op_spacing5(size_id), lvl);
780        DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
781                                big_op_spacing5(size_id), lvl);
782        DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
783                               big_op_spacing2(size_id), lvl);
784        DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
785                                big_op_spacing2(size_id), lvl);
786        DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
787                               big_op_spacing4(size_id), lvl);
788        DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
789                                big_op_spacing4(size_id), lvl);
790        DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
791                               big_op_spacing5(size_id), lvl);
792        DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
793                                big_op_spacing5(size_id), lvl);
794        DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
795                               4 * default_rule_thickness(size_id), lvl);
796        DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
797                                4 * default_rule_thickness(size_id), lvl);
798        /* All of the |space_after_script|s are done in |finalize_math_parameters| because the
799           \.{\\scriptspace} may have been altered by the user
800         */
801        DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id, 0,
802                               lvl);
803        DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id, 0,
804                                lvl);
805
806        DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
807                               big_op_spacing2(size_id), lvl);
808        DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
809                                big_op_spacing2(size_id), lvl);
810        DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
811                               big_op_spacing4(size_id), lvl);
812        DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
813                                big_op_spacing4(size_id), lvl);
814        DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
815                               big_op_spacing1(size_id), lvl);
816        DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
817                                big_op_spacing1(size_id), lvl);
818        DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
819                               big_op_spacing3(size_id), lvl);
820        DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
821                                big_op_spacing3(size_id), lvl);
822
823        /* The display-size |radical_vgap| is done twice because it needs
824           values from both the sy and the ex font. */
825        DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
826                                (default_rule_thickness(size_id) +
827                                 (abs(math_x_height(size_id)) / 4)), lvl);
828    }
829}
830
831@ This needs to be called just at the start of |mlist_to_hlist|, for
832backward compatibility with \.{\\scriptspace}.
833
834@c
835static void finalize_math_parameters(void)
836{
837    int saved_trace = int_par(tracing_assigns_code);
838    int_par(tracing_assigns_code) = 0;
839    if (get_math_param(math_param_space_after_script, display_style) ==
840        undefined_math_parameter) {
841        def_math_param(math_param_space_after_script, display_style,
842                       script_space, level_one);
843        def_math_param(math_param_space_after_script, text_style,
844                       script_space, level_one);
845        def_math_param(math_param_space_after_script, script_style,
846                       script_space, level_one);
847        def_math_param(math_param_space_after_script, script_script_style,
848                       script_space, level_one);
849        def_math_param(math_param_space_after_script, cramped_display_style,
850                       script_space, level_one);
851        def_math_param(math_param_space_after_script, cramped_text_style,
852                       script_space, level_one);
853        def_math_param(math_param_space_after_script, cramped_script_style,
854                       script_space, level_one);
855        def_math_param(math_param_space_after_script,
856                       cramped_script_script_style, script_space, level_one);
857    }
858    int_par(tracing_assigns_code) = saved_trace;
859
860}
861
862
863@ In order to convert mlists to hlists, i.e., noads to nodes, we need several
864subroutines that are conveniently dealt with now.
865
866Let us first introduce the macros that make it easy to get at the parameters and
867other font information. A size code, which is a multiple of 256, is added to a
868family number to get an index into the table of internal font numbers
869for each combination of family and size.  (Be alert: Size codes get
870larger as the type gets smaller.)
871
872@c
873static const char *math_size_string(int s)
874{
875    if (s == text_size)
876        return "textfont";
877    else if (s == script_size)
878        return "scriptfont";
879    else
880        return "scriptscriptfont";
881}
882
883@ When the style changes, the following piece of program computes associated
884information:
885
886@c
887#define setup_cur_size(a) do {                                   \
888        if (a==script_style ||                                          \
889            a==cramped_script_style)                                    \
890            cur_size=script_size;                                       \
891        else if (a==script_script_style ||                              \
892                 a==cramped_script_script_style)                        \
893            cur_size=script_script_size;                                \
894        else cur_size=text_size;                                        \
895    } while (0)
896
897
898@ a simple routine that creates a flat copy of a nucleus
899@c
900static pointer math_clone(pointer q)
901{
902    pointer x;
903    if (q == null)
904        return null;
905    x = new_node(type(q), 0);
906    reset_attributes(x, node_attr(q));
907    if (type(q) == math_char_node) {
908        math_fam(x) = math_fam(q);
909        math_character(x) = math_character(q);
910    } else {
911        math_list(x) = math_list(q);
912    }
913    return x;
914}
915
916
917
918
919@ Here is a function that returns a pointer to a rule node having a given
920  thickness |t|. The rule will extend horizontally to the boundary of the vlist
921  that eventually contains it.
922
923@c
924static pointer do_fraction_rule(scaled t, pointer att)
925{
926    pointer p;                  /* the new node */
927    p = new_rule();
928    rule_dir(p) = math_direction;
929    height(p) = t;
930    depth(p) = 0;
931    reset_attributes(p, att);
932    return p;
933}
934
935
936@  The |overbar| function returns a pointer to a vlist box that consists of
937  a given box |b|, above which has been placed a kern of height |k| under a
938  fraction rule of thickness |t| under additional space of height |ht|.
939
940@c
941static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att)
942{
943    pointer p, q;               /* nodes being constructed */
944    p = new_kern(k);
945    couple_nodes(p,b);
946    reset_attributes(p, att);
947    q = do_fraction_rule(t, att);
948    couple_nodes(q,p);
949    p = new_kern(ht);
950    reset_attributes(p, att);
951    couple_nodes(p,q);
952    q = vpackage(p, 0, additional, max_dimen, math_direction);
953    reset_attributes(q, att);
954    return q;
955}
956
957@ Here is a subroutine that creates a new box, whose list contains a
958  single character, and whose width includes the italic correction for
959  that character. The height or depth of the box will be negative, if
960  the height or depth of the character is negative; thus, this routine
961  may deliver a slightly different result than |hpack| would produce.
962
963@c
964static pointer char_box(internal_font_number f, int c, pointer bb)
965{
966    pointer b, p;               /* the new box and its character node */
967    b = new_null_box();
968    width(b) = char_width(f, c) + char_italic(f, c);
969    height(b) = char_height(f, c);
970    depth(b) = char_depth(f, c);
971    reset_attributes(b, bb);
972    p = new_char(f, c);
973    reset_attributes(p, bb);
974    list_ptr(b) = p;
975    return b;
976}
977
978@ Another handy subroutine computes the height plus depth of
979 a given character:
980
981@c
982static scaled height_plus_depth(internal_font_number f, int c)
983{
984    return (char_height(f, c) + char_depth(f, c));
985}
986
987
988@  When we build an extensible character, it's handy to have the
989  following subroutine, which puts a given character on top
990  of the characters already in box |b|:
991
992@c
993static scaled stack_into_box(pointer b, internal_font_number f, int c)
994{
995    pointer p, q;               /* new node placed into |b| */
996    p = char_box(f, c, node_attr(b));
997    if (type(b) == vlist_node) {
998//        vlink(p) = list_ptr(b);
999try_couple_nodes(p,list_ptr(b));
1000        list_ptr(b) = p;
1001        height(b) = height(p);
1002        if (width(b) < width(p))
1003            width(b) = width(p);
1004        return height_plus_depth(f, c);
1005    } else {
1006        q = list_ptr(b);
1007        if (q == null) {
1008            list_ptr(b) = p;
1009        } else {
1010            while (vlink(q) != null)
1011                q = vlink(q);
1012            couple_nodes(q,p);
1013        }
1014        if (height(b) < height(p))
1015            height(b) = height(p);
1016        if (depth(b) < depth(p))
1017            depth(b) = depth(p);
1018        return char_width(f, c);
1019    }
1020}
1021
1022
1023static void stack_glue_into_box(pointer b, scaled min, scaled max) {
1024    pointer p, q;               /* new node placed into |b| */
1025    q = new_spec(zero_glue);
1026    width(q) = min;
1027    stretch(q) = max-min;
1028    p = new_glue(q);
1029    reset_attributes(p, node_attr(b));
1030    if (type(b) == vlist_node) {
1031        try_couple_nodes(p,list_ptr(b));
1032        list_ptr(b) = p;
1033    } else {
1034        q = list_ptr(b);
1035        if (q == null) {
1036            list_ptr(b) = p;
1037        } else {
1038            while (vlink(q) != null)
1039                q = vlink(q);
1040            couple_nodes(q,p);
1041        }
1042    }
1043}
1044
1045@ \TeX's most important routine for dealing with formulas is called
1046 |mlist_to_hlist|.  After a formula has been scanned and represented
1047 as an mlist, this routine converts it to an hlist that can be placed
1048 into a box or incorporated into the text of a paragraph.  The
1049 explicit parameter |cur_mlist| points to the first node or noad in
1050 the given mlist (and it might be |null|); the parameter |penalties|
1051 is |true| if penalty nodes for potential line breaks are to be
1052 inserted into the resulting hlist, the parameter |cur_style| is a
1053 style code.  After |mlist_to_hlist| has acted, |vlink(temp_head)|
1054 points to the translated hlist.
1055
1056 Since mlists can be inside mlists, the procedure is recursive. And since this
1057 is not part of \TeX's inner loop, the program has been written in a manner
1058 that stresses compactness over efficiency.
1059@^recursion@>
1060
1061@c
1062int cur_size;                   /* size code corresponding to |cur_style|  */
1063
1064@ @c
1065static pointer get_delim_box(extinfo * ext, internal_font_number f, scaled v,
1066                             pointer att, int boxtype, int cur_style)
1067{
1068    pointer b;                  /* new box */
1069    scaled b_max;               /* natural (maximum) size of the stack */
1070    scaled s_max;               /* amount of possible shrink in the stack */
1071    extinfo *cur;
1072    scaled min_overlap, prev_overlap;
1073    int i;                      /* a temporary counter number of extensible pieces */
1074    int with_extenders;         /* number of times to repeat each repeatable item in |ext| */
1075    int num_extenders, num_normal;
1076    scaled a, c, d;
1077
1078    assert(ext != NULL);
1079    b = new_null_box();
1080    type(b) = (quarterword) boxtype;
1081    reset_attributes(b, att);
1082    min_overlap = connector_overlap_min(cur_style);
1083    assert(min_overlap >= 0);
1084    with_extenders = -1;
1085    num_extenders = 0;
1086    num_normal = 0;
1087
1088    cur = ext;
1089    while (cur != NULL) {
1090        if (!char_exists(f, cur->glyph)) {
1091            const char *hlp[] = {
1092                "Each glyph part in an extensible item should exist in the font.",
1093                "I will give up trying to find a suitable size for now. Fix your font!",
1094                NULL
1095            };
1096            tex_error("Variant part doesn't exist.", hlp);
1097            width(b) = null_delimiter_space;
1098            return b;
1099        }
1100        if (cur->extender > 0)
1101            num_extenders++;
1102        else
1103            num_normal++;
1104        /* no negative overlaps or advances are allowed */
1105        if (cur->start_overlap < 0 || cur->end_overlap < 0 || cur->advance < 0) {
1106            const char *hlp[] = {
1107                "All measurements in extensible items should be positive.",
1108                "To get around this problem, I have changed the font metrics.",
1109                "Fix your font!",
1110                NULL
1111            };
1112            tex_error("Extensible recipe has negative fields.", hlp);
1113            if (cur->start_overlap < 0)
1114                cur->start_overlap = 0;
1115            if (cur->end_overlap < 0)
1116                cur->end_overlap = 0;
1117            if (cur->advance < 0)
1118                cur->advance = 0;
1119        }
1120        cur = cur->next;
1121    }
1122    if (num_normal == 0) {
1123        const char *hlp[] = {
1124            "Each extensible recipe should have at least one non-repeatable part.",
1125            "To get around this problem, I have changed the first part to be",
1126            "non-repeatable. Fix your font!",
1127            NULL
1128        };
1129        tex_error("Extensible recipe has no fixed parts.", hlp);
1130        ext->extender = 0;
1131        num_normal = 1;
1132        num_extenders--;
1133    }
1134    /* |ext| holds a linked list of numerous items that may or may not be
1135       repeatable. For the total height, we have to figure out how many items
1136       are needed to create a stack of at least |v|.
1137       The next |while| loop does  that. It has two goals: it finds out
1138       the natural height |b_max|  of the all the parts needed to reach
1139       at least |v|,  and it sets |with_extenders| to the number of times
1140       each of the repeatable items in |ext| has to be repeated to reach
1141       that height.
1142     */
1143    cur = ext;
1144    b_max = 0;
1145    while (b_max < v && num_extenders > 0) {
1146        b_max = 0;
1147	prev_overlap = 0;
1148        with_extenders++;
1149        for (cur = ext; cur != NULL; cur = cur->next) {
1150	    if (cur->extender == 0) {
1151	        c = cur->start_overlap;
1152		if (min_overlap < c)
1153		    c = min_overlap;
1154		if (prev_overlap < c)
1155		    c = prev_overlap;
1156                a = cur->advance;
1157                if (a == 0) {
1158                    /* for tfm fonts */
1159                    if (boxtype == vlist_node)
1160                        a = height_plus_depth(f, cur->glyph);
1161                    else
1162                        a = char_width(f, cur->glyph);
1163                    assert(a >= 0);
1164                }
1165		b_max += a - c;
1166		prev_overlap = cur->end_overlap;
1167	    } else {
1168	        i = with_extenders;
1169		while (i > 0) {
1170	            c = cur->start_overlap;
1171		    if (min_overlap < c)
1172		        c = min_overlap;
1173		    if (prev_overlap < c)
1174		        c = prev_overlap;
1175                    a = cur->advance;
1176                    if (a == 0) {
1177                        /* for tfm fonts */
1178                        if (boxtype == vlist_node)
1179                            a = height_plus_depth(f, cur->glyph);
1180                        else
1181                            a = char_width(f, cur->glyph);
1182                        assert(a >= 0);
1183                    }
1184		    b_max += a - c;
1185		    prev_overlap = cur->end_overlap;
1186		    i--;
1187		}
1188	    }
1189	}
1190    }
1191
1192    /* assemble box using |with_extenders| copies of each extender, with
1193       appropriate glue wherever an overlap occurs */
1194    prev_overlap = 0;
1195    b_max = 0;
1196    s_max = 0;
1197    for (cur = ext; cur != NULL; cur = cur->next) {
1198        if (cur->extender == 0) {
1199	    c = cur->start_overlap;
1200	    if (prev_overlap < c)
1201	        c = prev_overlap;
1202	    d = c;
1203	    if (min_overlap < c)
1204	        c = min_overlap;
1205	    if (d > 0) {
1206	        stack_glue_into_box(b, -d, -c);
1207	        s_max += (-c) - (-d);
1208	        b_max -= d;
1209	    }
1210            b_max += stack_into_box(b, f, cur->glyph);
1211	    prev_overlap = cur->end_overlap;
1212	    i--;
1213	} else {
1214	    i = with_extenders;
1215	    while (i > 0) {
1216	        c = cur->start_overlap;
1217		if (prev_overlap < c)
1218		    c = prev_overlap;
1219		d = c;
1220		if (min_overlap < c)
1221		    c = min_overlap;
1222		if (d > 0) {
1223		    stack_glue_into_box(b, -d, -c);
1224	            s_max += (-c) - (-d);
1225	            b_max -= d;
1226		}
1227                b_max += stack_into_box(b, f, cur->glyph);
1228		prev_overlap = cur->end_overlap;
1229	        i--;
1230	    }
1231	}
1232    }
1233
1234    /* set glue so as to stretch the connections if needed */
1235    d = 0;
1236    if (v > b_max && s_max > 0) {
1237        d = v-b_max;
1238        /* don't stretch more than |s_max| */
1239        if (d > s_max)
1240            d = s_max;
1241        glue_order(b) = normal;
1242        glue_sign(b) = stretching;
1243        glue_set(b) = unfloat(d/(float) s_max);
1244        b_max += d;
1245    }
1246
1247    if (boxtype == vlist_node)
1248        height(b) = b_max;
1249    else
1250        width(b) = b_max;
1251
1252    return b;
1253}
1254
1255static pointer get_delim_vbox(extinfo * ext, internal_font_number f, scaled v,
1256                              pointer att, int cur_style)
1257{
1258    return get_delim_box(ext, f, v, att, vlist_node, cur_style);
1259}
1260
1261static pointer get_delim_hbox(extinfo * ext, internal_font_number f, scaled v,
1262                              pointer att, int cur_style)
1263{
1264    return get_delim_box(ext, f, v, att, hlist_node, cur_style);
1265}
1266
1267
1268
1269@  The |var_delimiter| function, which finds or constructs a sufficiently
1270  large delimiter, is the most interesting of the auxiliary functions that
1271  currently concern us. Given a pointer |d| to a delimiter field in some noad,
1272  together with a size code |s| and a vertical distance |v|, this function
1273  returns a pointer to a box that contains the smallest variant of |d| whose
1274  height plus depth is |v| or more. (And if no variant is large enough, it
1275  returns the largest available variant.) In particular, this routine will
1276  construct arbitrarily large delimiters from extensible components, if
1277  |d| leads to such characters.
1278
1279  The value returned is a box whose |shift_amount| has been set so that
1280  the box is vertically centered with respect to the axis in the given size.
1281  If a built-up symbol is returned, the height of the box before shifting
1282  will be the height of its topmost component.
1283
1284@c
1285static void endless_loop_error(internal_font_number g, int y)
1286{
1287    char s[256];
1288    const char *hlp[] = {
1289        "You managed to create a seemingly endless charlist chain in the current",
1290        "font. I have counted until 10000 already and still have not escaped, so"
1291            "I will jump out of the loop all by myself now. Fix your font!",
1292        NULL
1293    };
1294    snprintf(s, 256, "Math error: endless loop in charlist (U+%04x in %s)",
1295             (int) y, font_name(g));
1296    tex_error(s, hlp);
1297}
1298
1299static pointer do_var_delimiter(pointer d, int s, scaled v, scaled * ic,
1300                                boolean flat, int cur_style)
1301{
1302    /* label found,continue; */
1303    pointer b;                  /* the box that will be constructed */
1304    internal_font_number f, g;  /* best-so-far and tentative font codes */
1305    int c, i, x, y;             /* best-so-far and tentative character codes */
1306    scaled u;                   /* height-plus-depth of a tentative character */
1307    scaled w;                   /* largest height-plus-depth so far */
1308    int z;                      /* runs through font family members */
1309    boolean large_attempt;      /* are we trying the ``large'' variant? */
1310    pointer att;                /* to save the current attribute list */
1311    boolean do_parts;
1312    extinfo *ext;
1313    att = null;
1314    f = null_font;
1315    c = 0;
1316    w = 0;
1317    do_parts = false;
1318    large_attempt = false;
1319    if (d == null)
1320        goto FOUND;
1321    z = small_fam(d);
1322    x = small_char(d);
1323    i = 0;
1324    while (true) {
1325        /* The search process is complicated slightly by the facts that some of the
1326           characters might not be present in some of the fonts, and they might not
1327           be probed in increasing order of height. */
1328        if ((z != 0) || (x != 0)) {
1329            g = fam_fnt(z, s);
1330            if (g != null_font) {
1331                y = x;
1332              CONTINUE:
1333                i++;
1334                if (char_exists(g, y)) {
1335                    if (flat)
1336                        u = char_width(g, y);
1337                    else
1338                        u = height_plus_depth(g, y);
1339                    if (u > w) {
1340                        f = g;
1341                        c = y;
1342                        w = u;
1343                        if (u >= v)
1344                            goto FOUND;
1345                    }
1346                    if (char_tag(g, y) == ext_tag) {
1347                        f = g;
1348                        c = y;
1349                        do_parts = true;
1350                        goto FOUND;
1351                    }
1352                    if (i > 10000) {
1353                        /* endless loop */
1354                        endless_loop_error(g, y);
1355                        goto FOUND;
1356                    }
1357                    if (char_tag(g, y) == list_tag) {
1358                        y = char_remainder(g, y);
1359                        goto CONTINUE;
1360                    }
1361                }
1362            }
1363        }
1364        if (large_attempt)
1365            goto FOUND;         /* there were none large enough */
1366        large_attempt = true;
1367        z = large_fam(d);
1368        x = large_char(d);
1369    }
1370  FOUND:
1371    if (d != null) {
1372        att = node_attr(d);
1373        node_attr(d) = null;
1374        flush_node(d);
1375    }
1376    if (f != null_font) {
1377        /* When the following code is executed, |do_parts| will be true
1378           if a built-up symbol is supposed to be returned.
1379         */
1380        ext = NULL;
1381        if ((do_parts) &&
1382            ((!flat
1383              && (ext = get_charinfo_vert_variants(char_info(f, c))) != NULL)
1384             || (flat
1385                 && (ext =
1386                     get_charinfo_hor_variants(char_info(f, c))) != NULL))) {
1387            b = (flat ? get_delim_hbox(ext, f, v, att, cur_style) :
1388                 get_delim_vbox(ext, f, v, att, cur_style));
1389        } else {
1390            b = char_box(f, c, att);
1391        }
1392        /* This next test is because for OT MATH fonts, the italic correction of an
1393           extensible character is only used for the placement  of a subscript
1394           (in negated form), and it is not supposed to be added to the
1395           width of the character box at all.
1396
1397           This has an effect later on in |make_op| as well, where it has to do
1398           an extra correction for |make_script|'s addition of yet another italic
1399           correction.
1400         */
1401        if (!is_new_mathfont(f)) {
1402            width(b) += char_italic(f, c);
1403        }
1404        if (ic != NULL)
1405            *ic = char_italic(f, c);
1406    } else {
1407        b = new_null_box();
1408        reset_attributes(b, att);
1409        width(b) = (flat ? 0 : null_delimiter_space);   /* use this width if no delimiter was found */
1410        if (ic != NULL)
1411            *ic = 0;
1412    }
1413    if (!flat)
1414        shift_amount(b) = half(height(b) - depth(b)) - math_axis(s);
1415    delete_attribute_ref(att);
1416    return b;
1417}
1418
1419
1420static pointer var_delimiter(pointer d, int s, scaled v, scaled * ic,
1421                             int cur_style)
1422{
1423    return do_var_delimiter(d, s, v, ic, false, cur_style);
1424}
1425
1426static pointer flat_delimiter(pointer d, int s, scaled v, int cur_style)
1427{
1428    return do_var_delimiter(d, s, v, NULL, true, cur_style);
1429}
1430
1431@ The next subroutine is much simpler; it is used for numerators and
1432denominators of fractions as well as for displayed operators and
1433their limits above and below. It takes a given box~|b| and
1434changes it so that the new box is centered in a box of width~|w|.
1435The centering is done by putting \.{\\hss} glue at the left and right
1436of the list inside |b|, then packaging the new box; thus, the
1437actual box might not really be centered, if it already contains
1438infinite glue.
1439
1440
1441The given box might contain a single character whose italic correction
1442has been added to the width of the box; in this case a compensating
1443kern is inserted.
1444
1445@c
1446static pointer rebox(pointer b, scaled w)
1447{
1448    pointer p, q, r, att;       /* temporary registers for list manipulation */
1449    internal_font_number f;     /* font in a one-character box */
1450    scaled v;                   /* width of a character without italic correction */
1451
1452    if ((width(b) != w) && (list_ptr(b) != null)) {
1453        if (type(b) == vlist_node) {
1454            p = hpack(b, 0, additional, -1);
1455            reset_attributes(p, node_attr(b));
1456            b = p;
1457        }
1458        p = list_ptr(b);
1459        att = node_attr(b);
1460        add_node_attr_ref(att);
1461        if ((is_char_node(p)) && (vlink(p) == null)) {
1462            f = font(p);
1463            v = char_width(f, character(p));
1464            if (v != width(b)) {
1465                q = new_kern(width(b) - v);
1466                reset_attributes(q, att);
1467                couple_nodes(p,q);
1468            }
1469        }
1470        list_ptr(b) = null;
1471        flush_node(b);
1472        b = new_glue(ss_glue);
1473        reset_attributes(b, att);
1474        couple_nodes(b,p);
1475        while (vlink(p) != null)
1476            p = vlink(p);
1477        q = new_glue(ss_glue);
1478        reset_attributes(q, att);
1479        couple_nodes(p,q);
1480        r = hpack(b, w, exactly, -1);
1481        reset_attributes(r, att);
1482        delete_attribute_ref(att);
1483        return r;
1484    } else {
1485        width(b) = w;
1486        return b;
1487    }
1488}
1489
1490@ Here is a subroutine that creates a new glue specification from another
1491one that is expressed in `\.{mu}', given the value of the math unit.
1492
1493@c
1494#define mu_mult(A) mult_and_add(n,(A),xn_over_d((A),f,unity),max_dimen)
1495
1496static pointer math_glue(pointer g, scaled m)
1497{
1498    pointer p;                  /* the new glue specification */
1499    int n;                      /* integer part of |m| */
1500    scaled f;                   /* fraction part of |m| */
1501    n = x_over_n(m, unity);
1502    f = tex_remainder;
1503    if (f < 0) {
1504        decr(n);
1505        f = f + unity;
1506    }
1507    p = new_node(glue_spec_node, 0);
1508    width(p) = mu_mult(width(g));       /* convert \.{mu} to \.{pt} */
1509    stretch_order(p) = stretch_order(g);
1510    if (stretch_order(p) == normal)
1511        stretch(p) = mu_mult(stretch(g));
1512    else
1513        stretch(p) = stretch(g);
1514    shrink_order(p) = shrink_order(g);
1515    if (shrink_order(p) == normal)
1516        shrink(p) = mu_mult(shrink(g));
1517    else
1518        shrink(p) = shrink(g);
1519    return p;
1520}
1521
1522@ The |math_kern| subroutine removes |mu_glue| from a kern node, given
1523the value of the math unit.
1524
1525@c
1526static void math_kern(pointer p, scaled m)
1527{
1528    int n;                      /* integer part of |m| */
1529    scaled f;                   /* fraction part of |m| */
1530    if (subtype(p) == mu_glue) {
1531        n = x_over_n(m, unity);
1532        f = tex_remainder;
1533        if (f < 0) {
1534            decr(n);
1535            f = f + unity;
1536        }
1537        width(p) = mu_mult(width(p));
1538        subtype(p) = explicit;
1539    }
1540}
1541
1542@ @c
1543void run_mlist_to_hlist(halfword p, int mstyle, boolean penalties)
1544{
1545    int callback_id;
1546    int a, sfix;
1547    lua_State *L = Luas;
1548    if (p == null) {
1549        vlink(temp_head) = null;
1550        return;
1551    }
1552    finalize_math_parameters();
1553    callback_id = callback_defined(mlist_to_hlist_callback);
1554    if (callback_id > 0) {
1555        sfix = lua_gettop(L);
1556        if (!get_callback(L, callback_id)) {
1557            lua_settop(L, sfix);
1558            return;
1559        }
1560        alink(p) = null ;
1561        nodelist_to_lua(L, p);  /* arg 1 */
1562        lua_pushstring(L, math_style_names[mstyle]);    /* arg 2 */
1563        lua_pushboolean(L, penalties);  /* arg 3 */
1564        if (lua_pcall(L, 3, 1, 0) != 0) {       /* 3 args, 1 result */
1565            fprintf(stdout, "error: %s\n", lua_tostring(L, -1));
1566            lua_settop(L, sfix);
1567            error();
1568            return;
1569        }
1570        a = nodelist_from_lua(L);
1571	/* alink(vlink(a)) = null; */ /* hh-ls: not need to null here  */
1572        lua_settop(L, sfix);
1573        vlink(temp_head) = a;
1574    } else if (callback_id == 0) {
1575        mlist_to_hlist_args(p, mstyle, penalties);
1576    } else {
1577        vlink(temp_head) = null;
1578    }
1579}
1580
1581@ The recursion in |mlist_to_hlist| is due primarily to a subroutine
1582called |clean_box| that puts a given noad field into a box using a given
1583math style; |mlist_to_hlist| can call |clean_box|, which can call
1584|mlist_to_hlist|.
1585@^recursion@>
1586
1587
1588The box returned by |clean_box| is ``clean'' in the
1589sense that its |shift_amount| is zero.
1590
1591@c
1592static pointer clean_box(pointer p, int s, int cur_style)
1593{
1594    pointer q;                  /* beginning of a list to be boxed */
1595    pointer x;                  /* box to be returned */
1596    pointer r;                  /* temporary pointer */
1597    pointer mlist = null;       /* beginning of mlist to be translated */
1598    switch (type(p)) {
1599    case math_char_node:
1600        mlist = new_noad();
1601        r = math_clone(p);
1602        nucleus(mlist) = r;
1603        break;
1604    case sub_box_node:
1605        q = math_list(p);
1606        goto FOUND;
1607        break;
1608    case sub_mlist_node:
1609        mlist = math_list(p);
1610        break;
1611    default:
1612        q = new_null_box();
1613        goto FOUND;
1614    }
1615    mlist_to_hlist_args(mlist, s, false);
1616    q = vlink(temp_head);       /* recursive call */
1617    setup_cur_size(cur_style);
1618  FOUND:
1619    if (is_char_node(q) || (q == null))
1620        x = hpack(q, 0, additional, -1);
1621    else if ((vlink(q) == null) && (type(q) <= vlist_node)
1622             && (shift_amount(q) == 0))
1623        x = q;                  /* it's already clean */
1624    else
1625        x = hpack(q, 0, additional, -1);
1626    if (x != q && q != null)
1627        reset_attributes(x, node_attr(q));
1628    /* Here we save memory space in a common case. */
1629    q = list_ptr(x);
1630    if (is_char_node(q)) {
1631        r = vlink(q);
1632        if (r != null) {
1633            if (vlink(r) == null) {
1634                if (!is_char_node(r)) {
1635                    if (type(r) == kern_node) {
1636                        /* unneeded italic correction */
1637                        flush_node(r);
1638                        vlink(q) = null;
1639                    }
1640                }
1641            }
1642        }
1643    }
1644    return x;
1645}
1646
1647@ It is convenient to have a procedure that converts a |math_char|
1648field to an ``unpacked'' form. The |fetch| routine sets |cur_f| and |cur_c|
1649to the font code and character code of a given noad field.
1650It also takes care of issuing error messages for
1651nonexistent characters; in such cases, |char_exists(cur_f,cur_c)| will be |false|
1652after |fetch| has acted, and the field will also have been reset to |null|.
1653
1654The outputs of |fetch| are placed in global variables.
1655
1656@c
1657internal_font_number cur_f;     /* the |font| field of a |math_char| */
1658int cur_c;                      /* the |character| field of a |math_char| */
1659
1660static void fetch(pointer a)
1661{                               /* unpack the |math_char| field |a| */
1662    cur_c = math_character(a);
1663    cur_f = fam_fnt(math_fam(a), cur_size);
1664    if (cur_f == null_font) {
1665        char *msg;
1666        const char *hlp[] = {
1667            "Somewhere in the math formula just ended, you used the",
1668            "stated character from an undefined font family. For example,",
1669            "plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,",
1670            "and I'll try to forget that I needed that character.",
1671            NULL
1672        };
1673        msg = xmalloc(256);
1674        snprintf(msg, 255, "\\%s%d is undefined (character %d)",
1675                 math_size_string(cur_size), (int) math_fam(a), (int) cur_c);
1676        tex_error(msg, hlp);
1677        free(msg);
1678    } else {
1679        if (!(char_exists(cur_f, cur_c))) {
1680            char_warning(cur_f, cur_c);
1681        }
1682    }
1683}
1684
1685
1686@ We need to do a lot of different things, so |mlist_to_hlist| makes two
1687passes over the given mlist.
1688
1689The first pass does most of the processing: It removes ``mu'' spacing from
1690glue, it recursively evaluates all subsidiary mlists so that only the
1691top-level mlist remains to be handled, it puts fractions and square roots
1692and such things into boxes, it attaches subscripts and superscripts, and
1693it computes the overall height and depth of the top-level mlist so that
1694the size of delimiters for a |fence_noad| will be known.
1695The hlist resulting from each noad is recorded in that noad's |new_hlist|
1696field, an integer field that replaces the |nucleus| or |thickness|.
1697@^recursion@>
1698
1699The second pass eliminates all noads and inserts the correct glue and
1700penalties between nodes.
1701
1702@c
1703static void assign_new_hlist(pointer q, pointer r)
1704{
1705    switch (type(q)) {
1706    case fraction_noad:
1707        math_list(numerator(q)) = null;
1708        flush_node(numerator(q));
1709        numerator(q) = null;
1710        math_list(denominator(q)) = null;
1711        flush_node(denominator(q));
1712        denominator(q) = null;
1713        break;
1714    case radical_noad:
1715    case simple_noad:
1716    case accent_noad:
1717        if (nucleus(q) != null) {
1718            math_list(nucleus(q)) = null;
1719            flush_node(nucleus(q));
1720            nucleus(q) = null;
1721        }
1722        break;
1723    }
1724    new_hlist(q) = r;
1725}
1726
1727@ @c
1728#define choose_mlist(A) do { p=A(q); A(q)=null; } while (0)
1729
1730
1731@ Most of the actual construction work of |mlist_to_hlist| is done
1732by procedures with names
1733like |make_fraction|, |make_radical|, etc. To illustrate
1734the general setup of such procedures, let's begin with a couple of
1735simple ones.
1736
1737@c
1738static void make_over(pointer q, int cur_style)
1739{
1740    pointer p;
1741    p = overbar(clean_box(nucleus(q), cramped_style(cur_style), cur_style),
1742                overbar_vgap(cur_style),
1743                overbar_rule(cur_style),
1744                overbar_kern(cur_style), node_attr(nucleus(q)));
1745    math_list(nucleus(q)) = p;
1746    type(nucleus(q)) = sub_box_node;
1747}
1748
1749static void make_under(pointer q, int cur_style)
1750{
1751    pointer p, x, y, r;         /* temporary registers for box construction */
1752    scaled delta;               /* overall height plus depth */
1753    x = clean_box(nucleus(q), cur_style, cur_style);
1754    p = new_kern(underbar_vgap(cur_style));
1755    reset_attributes(p, node_attr(q));
1756    couple_nodes(x,p);
1757    r = do_fraction_rule(underbar_rule(cur_style), node_attr(q));
1758    couple_nodes(p,r);
1759    y = vpackage(x, 0, additional, max_dimen, math_direction);
1760    reset_attributes(y, node_attr(q));
1761    delta = height(y) + depth(y) + underbar_kern(cur_style);
1762    height(y) = height(x);
1763    depth(y) = delta - height(y);
1764    math_list(nucleus(q)) = y;
1765    type(nucleus(q)) = sub_box_node;
1766}
1767
1768static void make_vcenter(pointer q)
1769{
1770    pointer v;                  /* the box that should be centered vertically */
1771    scaled delta;               /* its height plus depth */
1772    v = math_list(nucleus(q));
1773    if (type(v) != vlist_node)
1774        confusion("vcenter");   /* this can't happen vcenter */
1775    delta = height(v) + depth(v);
1776    height(v) = math_axis(cur_size) + half(delta);
1777    depth(v) = delta - height(v);
1778}
1779
1780@ According to the rules in the \.{DVI} file specifications, we ensure alignment
1781@^square roots@>
1782between a square root sign and the rule above its nucleus by assuming that the
1783baseline of the square-root symbol is the same as the bottom of the rule. The
1784height of the square-root symbol will be the thickness of the rule, and the
1785depth of the square-root symbol should exceed or equal the height-plus-depth
1786of the nucleus plus a certain minimum clearance~|psi|. The symbol will be
1787placed so that the actual clearance is |psi| plus half the excess.
1788
1789@c
1790static void make_radical(pointer q, int cur_style)
1791{
1792    pointer x, y, p;            /* temporary registers for box construction */
1793    scaled delta, clr, theta, h;        /* dimensions involved in the calculation */
1794    x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
1795    clr = radical_vgap(cur_style);
1796    theta = radical_rule(cur_style);
1797    if (theta == undefined_math_parameter) {
1798        theta = fraction_rule(cur_style);
1799        y = var_delimiter(left_delimiter(q), cur_size,
1800                          height(x) + depth(x) + clr + theta, NULL, cur_style);
1801	/* If |y| is a composite then set |theta| to the height of its top
1802           character, else set it to the height of |y|. */
1803        if (list_ptr(y) != null
1804            && type(list_ptr(y)) == hlist_node
1805            && list_ptr(list_ptr(y)) != null
1806            && type(list_ptr(list_ptr(y))) == glyph_node) {     /* and it should be */
1807            theta = char_height(font(list_ptr(list_ptr(y))),
1808                           character(list_ptr(list_ptr(y))));
1809        } else {
1810            theta = height(y);
1811        }
1812    } else {
1813        y = var_delimiter(left_delimiter(q), cur_size,
1814                          height(x) + depth(x) + clr + theta, NULL, cur_style);
1815    }
1816    left_delimiter(q) = null;
1817    delta = (depth(y) + height(y) - theta) - (height(x) + depth(x) + clr);
1818    if (delta > 0)
1819        clr = clr + half(delta);        /* increase the actual clearance */
1820    shift_amount(y) = (height(y) - theta) - (height(x) + clr);
1821    h = depth(y) + height(y);
1822    p = overbar(x, clr, theta, radical_kern(cur_style), node_attr(y));
1823    couple_nodes(y,p);
1824    if (degree(q) != null) {
1825        scaled wr, br, ar;
1826        pointer r = clean_box(degree(q), script_script_style, cur_style);
1827        reset_attributes(r, node_attr(degree(q)));
1828        wr = width(r);
1829        if (wr == 0) {
1830            flush_node(r);
1831        } else {
1832            br = radical_degree_before(cur_style);
1833            ar = radical_degree_after(cur_style);
1834            if (-ar > (wr + br))
1835                ar = -(wr + br);
1836            x = new_kern(ar);
1837            reset_attributes(x, node_attr(degree(q)));
1838            couple_nodes(x,y);
1839            shift_amount(r) =
1840                -((xn_over_d(h, radical_degree_raise(cur_style), 100)) -
1841                  depth(y) - shift_amount(y));
1842            couple_nodes(r,x);
1843            x = new_kern(br);
1844            reset_attributes(x, node_attr(degree(q)));
1845            couple_nodes(x,r);
1846            y = x;
1847        }
1848        math_list(degree(q)) = null;    /* for \.{\\Uroot ..{<list>}{}} */
1849        flush_node(degree(q));
1850    }
1851    p = hpack(y, 0, additional, -1);
1852    reset_attributes(p, node_attr(q));
1853    math_list(nucleus(q)) = p;
1854    type(nucleus(q)) = sub_box_node;
1855}
1856
1857
1858@ Construct a vlist box
1859@c
1860static pointer
1861wrapup_delimiter(pointer x, pointer y, pointer q,
1862                 scaled shift_up, scaled shift_down)
1863{
1864    pointer p;                  /* temporary register for box construction */
1865    pointer v = new_null_box();
1866    type(v) = vlist_node;
1867    height(v) = shift_up + height(x);
1868    depth(v) = depth(y) + shift_down;
1869    reset_attributes(v, node_attr(q));
1870    p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
1871    reset_attributes(p, node_attr(q));
1872    couple_nodes(p,y);
1873    couple_nodes(x,p);
1874    list_ptr(v) = x;
1875    return v;
1876}
1877
1878@ @c
1879#define fixup_widths(x,y) do {                      \
1880        if (width(y) >= width(x)) {                 \
1881            width(x) = width(y);                    \
1882        } else {                                    \
1883            width(y) = width(x);                    \
1884        }                                           \
1885    } while (0)
1886
1887@ this has the |nucleus| box |x| as a limit above an extensible delimiter |y|
1888
1889@c
1890static void make_over_delimiter(pointer q, int cur_style)
1891{
1892    pointer x, y, v;            /* temporary registers for box construction */
1893    scaled shift_up, shift_down, clr, delta;
1894    x = clean_box(nucleus(q), sub_style(cur_style), cur_style);
1895    y = flat_delimiter(left_delimiter(q), cur_size, width(x), cur_style);
1896    left_delimiter(q) = null;
1897    fixup_widths(x, y);
1898    shift_up = over_delimiter_bgap(cur_style);
1899    shift_down = 0;
1900    clr = over_delimiter_vgap(cur_style);
1901    delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
1902    if (delta > 0) {
1903        shift_up = shift_up + delta;
1904    }
1905    v = wrapup_delimiter(x, y, q, shift_up, shift_down);
1906    width(v) = width(x);        /* this also equals |width(y)| */
1907    math_list(nucleus(q)) = v;
1908    type(nucleus(q)) = sub_box_node;
1909}
1910
1911@ this has the extensible delimiter |x| as a limit above |nucleus| box |y|
1912
1913@c
1914static void make_delimiter_over(pointer q, int cur_style)
1915{
1916    pointer x, y, v;            /* temporary registers for box construction */
1917    scaled shift_up, shift_down, clr, actual;
1918    y = clean_box(nucleus(q), cur_style, cur_style);
1919    x = flat_delimiter(left_delimiter(q),
1920                       cur_size + (cur_size == script_script_size ? 0 : 1),
1921                       width(y), cur_style);
1922    left_delimiter(q) = null;
1923    fixup_widths(x, y);
1924    shift_up = over_delimiter_bgap(cur_style)-height(x)-depth(x);
1925    shift_down = 0;
1926    clr = over_delimiter_vgap(cur_style);
1927    actual = shift_up - height(y);
1928    if (actual < clr) {
1929        shift_up = shift_up + (clr-actual);
1930    }
1931    v = wrapup_delimiter(x, y, q, shift_up, shift_down);
1932    width(v) = width(x);        /* this also equals |width(y)| */
1933    math_list(nucleus(q)) = v;
1934    type(nucleus(q)) = sub_box_node;
1935}
1936
1937
1938@ this has the extensible delimiter |y| as a limit below a |nucleus| box |x|
1939
1940@c
1941static void make_delimiter_under(pointer q, int cur_style)
1942{
1943    pointer x, y, v;            /* temporary registers for box construction */
1944    scaled shift_up, shift_down, clr, actual;
1945    x = clean_box(nucleus(q), cur_style, cur_style);
1946    y = flat_delimiter(left_delimiter(q),
1947                       cur_size + (cur_size == script_script_size ? 0 : 1),
1948                       width(x), cur_style);
1949    left_delimiter(q) = null;
1950    fixup_widths(x, y);
1951    shift_up = 0;
1952    shift_down = under_delimiter_bgap(cur_style) - height(y)-depth(y);
1953    clr = under_delimiter_vgap(cur_style);
1954    actual = shift_down - depth(x);
1955    if (actual<clr) {
1956       shift_down += (clr-actual);
1957    }
1958    v = wrapup_delimiter(x, y, q, shift_up, shift_down);
1959    width(v) = width(y);        /* this also equals |width(y)| */
1960    math_list(nucleus(q)) = v;
1961    type(nucleus(q)) = sub_box_node;
1962
1963}
1964
1965@ this has the extensible delimiter |x| as a limit below |nucleus| box |y|
1966
1967@c
1968static void make_under_delimiter(pointer q, int cur_style)
1969{
1970    pointer x, y, v;            /* temporary registers for box construction */
1971    scaled shift_up, shift_down, clr, delta;
1972    y = clean_box(nucleus(q), sup_style(cur_style), cur_style);
1973    x = flat_delimiter(left_delimiter(q), cur_size, width(y), cur_style);
1974    left_delimiter(q) = null;
1975    fixup_widths(x, y);
1976    shift_up = 0;
1977    shift_down = under_delimiter_bgap(cur_style);
1978    clr = under_delimiter_vgap(cur_style);
1979    delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
1980    if (delta > 0) {
1981        shift_down = shift_down + delta;
1982    }
1983    v = wrapup_delimiter(x, y, q, shift_up, shift_down);
1984    width(v) = width(y);        /* this also equals |width(y)| */
1985    math_list(nucleus(q)) = v;
1986    type(nucleus(q)) = sub_box_node;
1987
1988}
1989
1990
1991@ Slants are not considered when placing accents in math mode. The accenter is
1992centered over the accentee, and the accent width is treated as zero with
1993respect to the size of the final box.
1994
1995@c
1996#define TOP_CODE 1
1997#define BOT_CODE 2
1998#define TOP_OR_BOT_MASK ((TOP_CODE) | (BOT_CODE))
1999#define STRETCH_ACCENT_CODE 4
2000
2001static boolean compute_accent_skew(pointer q, int top_or_bot, scaled *s)
2002{
2003    pointer p;                  /* temporary register for box construction */
2004    boolean s_is_absolute;      /* will be true if a top-accent is placed in |s| */
2005
2006    s_is_absolute = false;
2007
2008    if (type(nucleus(q)) == math_char_node) {
2009        fetch(nucleus(q));
2010        if (is_new_mathfont(cur_f)) {
2011            if (top_or_bot == TOP_CODE) {
2012                *s = char_top_accent(cur_f, cur_c);
2013                if (*s != INT_MIN) {
2014                    s_is_absolute = true;
2015                }
2016            } else {
2017                *s = char_bot_accent(cur_f, cur_c);
2018                if (*s != INT_MIN) {
2019                    s_is_absolute = true;
2020                }
2021            }
2022        } else {
2023            if (top_or_bot == TOP_CODE) {
2024                *s = get_kern(cur_f, cur_c, skew_char(cur_f));
2025            } else {
2026                *s = 0;
2027            }
2028        }
2029    } else if (type(nucleus(q)) == sub_mlist_node) {
2030	/* if |nucleus(q)| is a |sub_mlist_node| composed of an |accent_noad| we
2031         * use the positioning of the nucleus of that noad, recursing until
2032         * the inner most |accent_noad|. This way multiple stacked accents are
2033         * aligned to the inner most one. */
2034        p = math_list(nucleus(q));
2035        if (type(p) == accent_noad) {
2036            s_is_absolute = compute_accent_skew(p, top_or_bot, s);
2037        }
2038    }
2039
2040    return s_is_absolute;
2041}
2042
2043static void do_make_math_accent(pointer q, internal_font_number f, int c,
2044                                int flags, int cur_style)
2045{
2046    pointer p, r, x, y;         /* temporary registers for box construction */
2047    scaled s;                   /* amount to skew the accent to the right */
2048    scaled h;                   /* height of character being accented */
2049    scaled delta;               /* space to remove between accent and accentee */
2050    scaled w;                   /* width of the accentee, not including sub/superscripts */
2051    boolean s_is_absolute;      /* will be true if a top-accent is placed in |s| */
2052    extinfo *ext;
2053    pointer attr_p;
2054    const int top_or_bot = flags & TOP_OR_BOT_MASK;
2055    attr_p = (top_or_bot == TOP_CODE ? accent_chr(q) : bot_accent_chr(q));
2056    c = cur_c;
2057    f = cur_f;
2058
2059    s = 0;
2060    s_is_absolute = false;
2061    /* Compute the amount of skew, or set |s| to an alignment point */
2062    s_is_absolute = compute_accent_skew(q, top_or_bot, &s);
2063
2064    x = clean_box(nucleus(q), cramped_style(cur_style), cur_style);
2065    w = width(x);
2066    h = height(x);
2067    if (is_new_mathfont(cur_f) && !s_is_absolute) {
2068      s = half(w);
2069      s_is_absolute = true;
2070    }
2071    /* Switch to a larger accent if available and appropriate */
2072    y = null;
2073    ext = NULL;
2074    if ((flags & STRETCH_ACCENT_CODE) && (char_width(f, c) < w)) {
2075      while (1) {
2076        if ((char_tag(f, c) == ext_tag) &&
2077            ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) {
2078            y = get_delim_hbox(ext, f, w, node_attr(attr_p), cur_style);
2079            break;
2080        } else if (char_tag(f, c) != list_tag) {
2081            break;
2082        } else {
2083            int yy = char_remainder(f, c);
2084            if (!char_exists(f, yy))
2085                break;
2086            if (char_width(f, yy) > w)
2087                break;
2088            c = yy;
2089        }
2090      }
2091    }
2092    if (y == null) {
2093        y = char_box(f, c, node_attr(attr_p));
2094    }
2095    if (top_or_bot == TOP_CODE) {
2096        if (h < accent_base_height(f))
2097            delta = h;
2098        else
2099            delta = accent_base_height(f);
2100    } else {
2101        delta = 0;              /* hm */
2102    }
2103    if ((supscr(q) != null) || (subscr(q) != null)) {
2104        if (type(nucleus(q)) == math_char_node) {
2105            /* Swap the subscript and superscript into box |x| */
2106            flush_node_list(x);
2107            x = new_noad();
2108            r = math_clone(nucleus(q));
2109            nucleus(x) = r;
2110            supscr(x) = supscr(q);
2111            supscr(q) = null;
2112            subscr(x) = subscr(q);
2113            subscr(q) = null;
2114            type(nucleus(q)) = sub_mlist_node;
2115            math_list(nucleus(q)) = x;
2116            x = clean_box(nucleus(q), cur_style, cur_style);
2117            delta = delta + height(x) - h;
2118            h = height(x);
2119        }
2120    }
2121    if (s_is_absolute) {
2122        scaled sa;
2123        if (ext != NULL) {
2124            sa = half(width(y));	/* if the accent is extensible just take the center */
2125        } else {
2126            sa = char_top_accent(f, c);
2127        }
2128        if (sa == INT_MIN) {
2129            sa = half(width(y));        /* just take the center */
2130        }
2131        shift_amount(y) = s - sa;
2132    } else {
2133        if (width(y)== 0) {
2134            shift_amount(y) = s + w;
2135        } else {
2136            shift_amount(y) = s + half(w - width(y));
2137        }
2138    }
2139    width(y) = 0;
2140    if (top_or_bot == TOP_CODE) {
2141        p = new_kern(-delta);
2142        couple_nodes(p,x);
2143        couple_nodes(y,p);
2144    } else {
2145#if 0
2146           p = new_kern(-delta);
2147           couple_nodes(x,p);
2148           couple_nodes(p,y);
2149           y = x;
2150#endif
2151        couple_nodes(x,y);
2152        y = x;
2153    }
2154    r = vpackage(y, 0, additional, max_dimen, math_direction);
2155    reset_attributes(r, node_attr(q));
2156    width(r) = width(x);
2157    y = r;
2158    if (top_or_bot == TOP_CODE) {
2159        if (height(y) < h) {
2160            /* Make the height of box |y| equal to |h| */
2161            p = new_kern(h - height(y));
2162            reset_attributes(p, node_attr(q));
2163//            vlink(p) = list_ptr(y);
2164try_couple_nodes(p,list_ptr(y));
2165            list_ptr(y) = p;
2166            height(y) = h;
2167        }
2168    } else {
2169        shift_amount(y) = -(h - height(y));
2170    }
2171    math_list(nucleus(q)) = y;
2172    type(nucleus(q)) = sub_box_node;
2173}
2174
2175static void make_math_accent(pointer q, int cur_style)
2176{
2177    int topstretch = !(subtype(q) % 2);
2178    int botstretch = !(subtype(q) / 2);
2179
2180    if (accent_chr(q) != null) {
2181        fetch(accent_chr(q));
2182        if (char_exists(cur_f, cur_c)) {
2183          do_make_math_accent(q, cur_f, cur_c, TOP_CODE | (topstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
2184        }
2185        flush_node(accent_chr(q));
2186        accent_chr(q) = null;
2187    }
2188    if (bot_accent_chr(q) != null) {
2189        fetch(bot_accent_chr(q));
2190        if (char_exists(cur_f, cur_c)) {
2191          do_make_math_accent(q, cur_f, cur_c, BOT_CODE | (botstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
2192        }
2193        flush_node(bot_accent_chr(q));
2194        bot_accent_chr(q) = null;
2195    }
2196}
2197
2198@ The |make_fraction| procedure is a bit different because it sets
2199|new_hlist(q)| directly rather than making a sub-box.
2200
2201@c
2202static void make_fraction(pointer q, int cur_style)
2203{
2204    pointer p, v, x, y, z;      /* temporary registers for box construction */
2205    scaled delta, delta1, delta2, shift_up, shift_down, clr;
2206    /* dimensions for box calculations */
2207    if (thickness(q) == default_code)
2208        thickness(q) = fraction_rule(cur_style);
2209    /* Create equal-width boxes |x| and |z| for the numerator and denominator,
2210       and compute the default amounts |shift_up| and |shift_down| by which they
2211       are displaced from the baseline */
2212    x = clean_box(numerator(q), num_style(cur_style), cur_style);
2213    z = clean_box(denominator(q), denom_style(cur_style), cur_style);
2214    if (width(x) < width(z))
2215        x = rebox(x, width(z));
2216    else
2217        z = rebox(z, width(x));
2218    if (thickness(q) == 0) {
2219        shift_up = stack_num_up(cur_style);
2220        shift_down = stack_denom_down(cur_style);
2221        /* The numerator and denominator must be separated by a certain minimum
2222           clearance, called |clr| in the following program. The difference between
2223           |clr| and the actual clearance is |2delta|. */
2224        clr = stack_vgap(cur_style);
2225        delta = half(clr - ((shift_up - depth(x)) - (height(z) - shift_down)));
2226        if (delta > 0) {
2227            shift_up = shift_up + delta;
2228            shift_down = shift_down + delta;
2229        }
2230    } else {
2231        shift_up = fraction_num_up(cur_style);
2232        shift_down = fraction_denom_down(cur_style);
2233        /* In the case of a fraction line, the minimum clearance depends on the actual
2234           thickness of the line. */
2235        delta = half(thickness(q));
2236        clr = fraction_num_vgap(cur_style);
2237        clr = ext_xn_over_d(clr, thickness(q), fraction_rule(cur_style));
2238        delta1 = clr - ((shift_up - depth(x)) - (math_axis(cur_size) + delta));
2239        if (delta1 > 0)
2240            shift_up = shift_up + delta1;
2241        clr = fraction_denom_vgap(cur_style);
2242        clr = ext_xn_over_d(clr, thickness(q), fraction_rule(cur_style));
2243        delta2 =
2244            clr - ((math_axis(cur_size) - delta) - (height(z) - shift_down));
2245        if (delta2 > 0)
2246            shift_down = shift_down + delta2;
2247    }
2248    /* Construct a vlist box for the fraction, according to |shift_up| and |shift_down| */
2249    v = new_null_box();
2250    type(v) = vlist_node;
2251    height(v) = shift_up + height(x);
2252    depth(v) = depth(z) + shift_down;
2253    width(v) = width(x);        /* this also equals |width(z)| */
2254    reset_attributes(v, node_attr(q));
2255    if (thickness(q) == 0) {
2256        p = new_kern((shift_up - depth(x)) - (height(z) - shift_down));
2257        couple_nodes(p,z);
2258    } else {
2259        y = do_fraction_rule(thickness(q), node_attr(q));
2260        p = new_kern((math_axis(cur_size) - delta) - (height(z) - shift_down));
2261        reset_attributes(p, node_attr(q));
2262        couple_nodes(y,p);
2263        couple_nodes(p,z);
2264        p = new_kern((shift_up - depth(x)) - (math_axis(cur_size) + delta));
2265        couple_nodes(p,y);
2266    }
2267    reset_attributes(p, node_attr(q));
2268    couple_nodes(x,p);
2269    list_ptr(v) = x;
2270    /* Put the fraction into a box with its delimiters, and make |new_hlist(q)|
2271       point to it */
2272    delta = fraction_del_size(cur_style);
2273    x = var_delimiter(left_delimiter(q), cur_size, delta, NULL, cur_style);
2274    left_delimiter(q) = null;
2275    couple_nodes(x,v);
2276    z = var_delimiter(right_delimiter(q), cur_size, delta, NULL, cur_style);
2277    right_delimiter(q) = null;
2278    couple_nodes(v,z);
2279    y = hpack(x, 0, additional, -1);
2280    reset_attributes(y, node_attr(q));
2281    assign_new_hlist(q, y);
2282}
2283
2284
2285@ If the nucleus of an |op_noad| is a single character, it is to be
2286centered vertically with respect to the axis, after first being enlarged
2287(via a character list in the font) if we are in display style.  The normal
2288convention for placing displayed limits is to put them above and below the
2289operator in display style.
2290
2291The italic correction is removed from the character if there is a subscript
2292and the limits are not being displayed. The |make_op|
2293routine returns the value that should be used as an offset between
2294subscript and superscript.
2295
2296After |make_op| has acted, |subtype(q)| will be |limits| if and only if
2297the limits have been set above and below the operator. In that case,
2298|new_hlist(q)| will already contain the desired final box.
2299
2300@c
2301static scaled make_op(pointer q, int cur_style)
2302{
2303    scaled delta;               /* offset between subscript and superscript */
2304    pointer p, v, x, y, z;      /* temporary registers for box construction */
2305    int c;                      /* register for character examination */
2306    scaled shift_up, shift_down;        /* dimensions for box calculation */
2307    scaled ok_size;
2308    if ((subtype(q) == op_noad_type_normal) && (cur_style < text_style))
2309        subtype(q) = op_noad_type_limits;
2310    if (type(nucleus(q)) == math_char_node) {
2311        fetch(nucleus(q));
2312        if (cur_style < text_style) {   /* try to make it larger */
2313            ok_size = minimum_operator_size(cur_style);
2314            if (ok_size != undefined_math_parameter) {
2315                /* creating a temporary delimiter is the cleanest way */
2316                y = new_node(delim_node, 0);
2317                small_fam(y) = math_fam(nucleus(q));
2318                small_char(y) = math_character(nucleus(q));
2319                x = var_delimiter(y, text_size, ok_size, &delta, cur_style);
2320                if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
2321                    width(x) -= delta;  /* remove italic correction */
2322                }
2323                /* For an OT MATH font, we may have to get rid of yet another italic
2324                   correction because |make_scripts()| will add one.
2325                   This test is somewhat more complicated because |x| can be a null
2326                   delimiter */
2327                if ((subscr(q) != null || supscr(q) != null)
2328                    && (subtype(q) != op_noad_type_limits)
2329                    && ((list_ptr(x) != null)
2330                        && (type(list_ptr(x)) == glyph_node)
2331                        && is_new_mathfont(font(list_ptr(x))))) {
2332                    width(x) -= delta;  /* remove another italic correction */
2333                }
2334            } else {
2335                ok_size = height_plus_depth(cur_f, cur_c) + 1;
2336                while ((char_tag(cur_f, cur_c) == list_tag) &&
2337                       height_plus_depth(cur_f, cur_c) < ok_size) {
2338                    c = char_remainder(cur_f, cur_c);
2339                    if (!char_exists(cur_f, c))
2340                        break;
2341                    cur_c = c;
2342                    math_character(nucleus(q)) = c;
2343                }
2344                delta = char_italic(cur_f, cur_c);
2345                x = clean_box(nucleus(q), cur_style, cur_style);
2346                if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits))
2347                    width(x) = width(x) - delta;        /* remove italic correction */
2348
2349                shift_amount(x) =
2350                    half(height(x) - depth(x)) - math_axis(cur_size);
2351                /* center vertically */
2352            }
2353            type(nucleus(q)) = sub_box_node;
2354            math_list(nucleus(q)) = x;
2355
2356        } else {                /* normal size */
2357            delta = char_italic(cur_f, cur_c);
2358            x = clean_box(nucleus(q), cur_style, cur_style);
2359            if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits))
2360                width(x) = width(x) - delta;    /* remove italic correction */
2361
2362            /* For an OT MATH font, we may have to get rid of yet another italic
2363               correction because |make_scripts()| will add one.
2364               This test is somewhat more complicated because |x| can be a null
2365               delimiter */
2366            if ((subscr(q) != null || supscr(q) != null)
2367                && (subtype(q) != op_noad_type_limits)
2368                && ((list_ptr(x) != null)
2369                    && (type(list_ptr(x)) == glyph_node)
2370                    && is_new_mathfont(font(list_ptr(x))))) {
2371                width(x) -= delta;  /* remove another italic correction */
2372	    }
2373
2374            shift_amount(x) = half(height(x) - depth(x)) - math_axis(cur_size);
2375            /* center vertically */
2376            type(nucleus(q)) = sub_box_node;
2377            math_list(nucleus(q)) = x;
2378        }
2379    } else {
2380        delta = 0;
2381    }
2382    if (subtype(q) == op_noad_type_limits) {
2383        /* The following program builds a vlist box |v| for displayed limits. The
2384           width of the box is not affected by the fact that the limits may be skewed. */
2385        x = clean_box(supscr(q), sup_style(cur_style), cur_style);
2386        y = clean_box(nucleus(q), cur_style, cur_style);
2387        z = clean_box(subscr(q), sub_style(cur_style), cur_style);
2388        v = new_null_box();
2389        reset_attributes(v, node_attr(q));
2390        type(v) = vlist_node;
2391        width(v) = width(y);
2392        if (width(x) > width(v))
2393            width(v) = width(x);
2394        if (width(z) > width(v))
2395            width(v) = width(z);
2396        x = rebox(x, width(v));
2397        y = rebox(y, width(v));
2398        z = rebox(z, width(v));
2399        shift_amount(x) = half(delta);
2400        shift_amount(z) = -shift_amount(x);
2401        height(v) = height(y);
2402        depth(v) = depth(y);
2403        /* Attach the limits to |y| and adjust |height(v)|, |depth(v)| to
2404           account for their presence */
2405        /* We use |shift_up| and |shift_down| in the following program for the
2406           amount of glue between the displayed operator |y| and its limits |x| and
2407           |z|. The vlist inside box |v| will consist of |x| followed by |y| followed
2408           by |z|, with kern nodes for the spaces between and around them. */
2409        if (supscr(q) == null) {
2410            list_ptr(x) = null;
2411            flush_node(x);
2412            list_ptr(v) = y;
2413        } else {
2414            shift_up = limit_above_bgap(cur_style) - depth(x);
2415            if (shift_up < limit_above_vgap(cur_style))
2416                shift_up = limit_above_vgap(cur_style);
2417            p = new_kern(shift_up);
2418            reset_attributes(p, node_attr(q));
2419            couple_nodes(p,y);
2420            couple_nodes(x,p);
2421            p = new_kern(limit_above_kern(cur_style));
2422            reset_attributes(p, node_attr(q));
2423            couple_nodes(p,x);
2424            list_ptr(v) = p;
2425            height(v) =
2426                height(v) + limit_above_kern(cur_style) + height(x) + depth(x) +
2427                shift_up;
2428        }
2429        if (subscr(q) == null) {
2430            list_ptr(z) = null;
2431            flush_node(z);
2432        } else {
2433            shift_down = limit_below_bgap(cur_style) - height(z);
2434            if (shift_down < limit_below_vgap(cur_style))
2435                shift_down = limit_below_vgap(cur_style);
2436            p = new_kern(shift_down);
2437            reset_attributes(p, node_attr(q));
2438            couple_nodes(y,p);
2439            couple_nodes(p,z);
2440            p = new_kern(limit_below_kern(cur_style));
2441            reset_attributes(p, node_attr(q));
2442            couple_nodes(z,p);
2443            depth(v) =
2444                depth(v) + limit_below_kern(cur_style) + height(z) + depth(z) +
2445                shift_down;
2446        }
2447        if (subscr(q) != null) {
2448            math_list(subscr(q)) = null;
2449            flush_node(subscr(q));
2450            subscr(q) = null;
2451        }
2452        if (supscr(q) != null) {
2453            math_list(supscr(q)) = null;
2454            flush_node(supscr(q));
2455            supscr(q) = null;
2456        }
2457        assign_new_hlist(q, v);
2458    }
2459    return delta;
2460}
2461
2462@ A ligature found in a math formula does not create a ligature, because
2463there is no question of hyphenation afterwards; the ligature will simply be
2464stored in an ordinary |glyph_node|, after residing in an |ord_noad|.
2465
2466The |type| is converted to |math_text_char| here if we would not want to
2467apply an italic correction to the current character unless it belongs
2468to a math font (i.e., a font with |space=0|).
2469
2470No boundary characters enter into these ligatures.
2471
2472@c
2473static void make_ord(pointer q)
2474{
2475    int a;                      /* the left-side character for lig/kern testing */
2476    pointer p, r, s;            /* temporary registers for list manipulation */
2477    scaled k;                   /* a kern */
2478    liginfo lig;                /* a ligature */
2479  RESTART:
2480    if (subscr(q) == null &&
2481        supscr(q) == null && type(nucleus(q)) == math_char_node) {
2482        p = vlink(q);
2483        if ((p != null) &&
2484            (type(p) == simple_noad) &&
2485            (subtype(p) <= punct_noad_type) &&
2486            (type(nucleus(p)) == math_char_node) &&
2487            (math_fam(nucleus(p)) == math_fam(nucleus(q)))) {
2488            type(nucleus(q)) = math_text_char_node;
2489            fetch(nucleus(q));
2490            a = cur_c;
2491            if ((has_kern(cur_f, a)) || (has_lig(cur_f, a))) {
2492                cur_c = math_character(nucleus(p));
2493                /* If character |a| has a kern with |cur_c|, attach
2494                   the kern after~|q|; or if it has a ligature with |cur_c|, combine
2495                   noads |q| and~|p| appropriately; then |return| if the cursor has
2496                   moved past a noad, or |goto restart| */
2497
2498                /* Note that a ligature between an |ord_noad| and another kind of noad
2499                   is replaced by an |ord_noad|, when the two noads collapse into one.
2500                   But we could make a parenthesis (say) change shape when it follows
2501                   certain letters. Presumably a font designer will define such
2502                   ligatures only when this convention makes sense. */
2503
2504                if (disable_lig == 0 && has_lig(cur_f, a)) {
2505                    lig = get_ligature(cur_f, a, cur_c);
2506                    if (is_valid_ligature(lig)) {
2507                        check_interrupt();      /* allow a way out of infinite ligature loop */
2508                        switch (lig_type(lig)) {
2509                        case 1:
2510                        case 5:
2511                            math_character(nucleus(q)) = lig_replacement(lig);  /* \.{=:\char`\|}, \.{=:\char`\|>} */
2512                            break;
2513                        case 2:
2514                        case 6:
2515                            math_character(nucleus(p)) = lig_replacement(lig);  /* \.{\char`\|=:}, \.{\char`\|=:>} */
2516                            break;
2517                        case 3:
2518                        case 7:
2519                        case 11:
2520                            r = new_noad();     /* \.{\char`\|=:\char`\|}, \.{\char`\|=:\char`\|>}, \.{\char`\|=:\char`\|>>} */
2521                            reset_attributes(r, node_attr(q));
2522                            s = new_node(math_char_node, 0);
2523                            reset_attributes(s, node_attr(q));
2524                            nucleus(r) = s;
2525                            math_character(nucleus(r)) = lig_replacement(lig);
2526                            math_fam(nucleus(r)) = math_fam(nucleus(q));
2527                            couple_nodes(q,r);
2528                            couple_nodes(r,p);
2529                            if (lig_type(lig) < 11)
2530                                type(nucleus(r)) = math_char_node;
2531                            else
2532                                type(nucleus(r)) = math_text_char_node; /* prevent combination */
2533                            break;
2534                        default:
2535                            try_couple_nodes(q,vlink(p));
2536                            math_character(nucleus(q)) = lig_replacement(lig);  /* \.{=:} */
2537                            s = math_clone(subscr(p));
2538                            subscr(q) = s;
2539                            s = math_clone(supscr(p));
2540                            supscr(q) = s;
2541                            math_reset(subscr(p));      /* just in case */
2542                            math_reset(supscr(p));
2543                            flush_node(p);
2544                            break;
2545                        }
2546                        if (lig_type(lig) > 3)
2547                            return;
2548                        type(nucleus(q)) = math_char_node;
2549                        goto RESTART;
2550                    }
2551                }
2552                if (disable_kern == 0 && has_kern(cur_f, a)) {
2553                    k = get_kern(cur_f, a, cur_c);      /* todo: should this use mathkerns? */
2554                    if (k != 0) {
2555                        p = new_kern(k);
2556                        reset_attributes(p, node_attr(q));
2557                        couple_nodes(p,vlink(q));
2558                        couple_nodes(q,p);
2559                        return;
2560                    }
2561                }
2562            }
2563        }
2564    }
2565}
2566
2567@ If the fonts for the left and right bits of a mathkern are not
2568both new-style fonts, then return a sentinel value meaning:
2569please use old-style italic correction placement
2570
2571@c
2572#define MATH_KERN_NOT_FOUND 0x7FFFFFFF
2573
2574@ This function tries to find the kern needed for proper cut-ins.
2575   The left side doesn't move, but the right side does, so the first
2576   order of business is to create a staggered fence line on the
2577   left side of the right character.
2578
2579   The microsoft spec says that there are four quadrants, but the
2580   actual images say
2581
2582@c
2583static scaled math_kern_at(internal_font_number f, int c, int side, int v)
2584{
2585    int h, k, numkerns;
2586    scaled *kerns_heights;
2587    scaled kern = 0;
2588    charinfo *co = char_info(f, c);     /* known to exist */
2589    numkerns = get_charinfo_math_kerns(co, side);
2590#ifdef DEBUG
2591    fprintf(stderr, "  entries = %d, height = %d\n", numkerns, v);
2592#endif
2593    if (numkerns == 0)
2594        return kern;
2595    if (side == top_left_kern) {
2596        kerns_heights = co->top_left_math_kern_array;
2597    } else if (side == bottom_left_kern) {
2598        kerns_heights = co->bottom_left_math_kern_array;
2599    } else if (side == top_right_kern) {
2600        kerns_heights = co->top_right_math_kern_array;
2601    } else if (side == bottom_right_kern) {
2602        kerns_heights = co->bottom_right_math_kern_array;
2603    } else {
2604        confusion("math_kern_at");
2605        kerns_heights = NULL;   /* not reached */
2606    }
2607#ifdef DEBUG
2608    fprintf(stderr, "   entry 0: %d,%d\n", kerns_heights[0], kerns_heights[1]);
2609#endif
2610    if (v < kerns_heights[0])
2611        return kerns_heights[1];
2612    for (k = 0; k < numkerns; k++) {
2613        h = kerns_heights[(k * 2)];
2614        kern = kerns_heights[(k * 2) + 1];
2615#ifdef DEBUG
2616        if (k > 0)
2617            fprintf(stderr, "   entry %d: %d,%d\n", k, h, kern);
2618#endif
2619        if (h > v) {
2620            return kern;
2621        }
2622    }
2623    return kern;
2624}
2625
2626@ @c
2627static scaled
2628find_math_kern(internal_font_number l_f, int l_c,
2629               internal_font_number r_f, int r_c, int cmd, scaled shift)
2630{
2631    scaled corr_height_top = 0, corr_height_bot = 0;
2632    scaled krn_l = 0, krn_r = 0, krn = 0;
2633    if ((!is_new_mathfont(l_f)) || (!is_new_mathfont(r_f))
2634        || (!char_exists(l_f, l_c)) || (!char_exists(r_f, r_c)))
2635        return MATH_KERN_NOT_FOUND;
2636
2637    if (cmd == sup_mark_cmd) {
2638        corr_height_top = char_height(l_f, l_c);
2639        corr_height_bot = -char_depth(r_f, r_c) + shift;        /* bottom of superscript */
2640        krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_top);
2641        krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_top);
2642#ifdef DEBUG
2643        fprintf(stderr, "SUPER Top LR = %d,%d (shift %d)\n", krn_l, krn_r,
2644                shift);
2645#endif
2646        krn = (krn_l + krn_r);
2647        krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_bot);
2648        krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_bot);
2649#ifdef DEBUG
2650        fprintf(stderr, "SUPER Bot LR = %d,%d\n", krn_l, krn_r);
2651#endif
2652        if ((krn_l + krn_r) < krn)
2653            krn = (krn_l + krn_r);
2654        return (krn);
2655
2656    } else if (cmd == sub_mark_cmd) {
2657        corr_height_top = char_height(r_f, r_c) - shift;        /* top of subscript */
2658        corr_height_bot = -char_depth(l_f, l_c);
2659        krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_top);
2660        krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_top);
2661#ifdef DEBUG
2662        fprintf(stderr, "SUB Top LR = %d,%d\n", krn_l, krn_r);
2663#endif
2664        krn = (krn_l + krn_r);
2665        krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_bot);
2666        krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_bot);
2667#ifdef DEBUG
2668        fprintf(stderr, "SUB Bot LR = %d,%d\n", krn_l, krn_r);
2669#endif
2670        if ((krn_l + krn_r) < krn)
2671            krn = (krn_l + krn_r);
2672        return (krn);
2673
2674    } else {
2675        confusion("find_math_kern");
2676    }
2677    return 0;                   /* not reached */
2678}
2679
2680@ just a small helper
2681@c
2682static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2)
2683{
2684    pointer y;
2685    pointer z = new_kern(delta2);
2686    if (new_hlist(q) == null) { /* this is somewhat weird */
2687        new_hlist(q) = z;
2688    } else {
2689        y = new_hlist(q);
2690        while (vlink(y) != null)
2691            y = vlink(y);
2692        couple_nodes(y,z);
2693    }
2694    return new_hlist(q);
2695}
2696
2697@
2698@c
2699#ifdef DEBUG
2700void dump_simple_field(pointer q)
2701{
2702    pointer p;
2703    printf("   [%d,  type=%d, vlink=%d] ", q, type(q), vlink(q));
2704    switch (type(q)) {
2705    case math_char_node:
2706        printf("mathchar ");
2707        break;
2708    case math_text_char_node:
2709        printf("texchar ");
2710        break;
2711    case sub_box_node:
2712        printf("box ");
2713        break;
2714    case sub_mlist_node:
2715        printf("mlist ");
2716        p = math_list(q);
2717        while (p != null) {
2718            dump_simple_field(p);
2719            p = vlink(p);
2720        }
2721        break;
2722    }
2723}
2724
2725
2726void dump_simple_node(pointer q)
2727{
2728    printf("node %d, type=%d, vlink=%d\n", q, type(q), vlink(q));
2729    printf("nucleus: ");
2730    dump_simple_field(nucleus(q));
2731    printf("\n");
2732    printf("sub: ");
2733    dump_simple_field(subscr(q));
2734    printf("\n");
2735    printf("sup: ");
2736    dump_simple_field(supscr(q));
2737    printf("\n\n");
2738}
2739#endif
2740
2741@ The purpose of |make_scripts(q,it)| is to attach the subscript and/or
2742superscript of noad |q| to the list that starts at |new_hlist(q)|,
2743given that subscript and superscript aren't both empty. The superscript
2744will be horizontally shifted over |delta1|, the subscript over |delta2|.
2745
2746We set |shift_down| and |shift_up| to the minimum amounts to shift the
2747baseline of subscripts and superscripts based on the given nucleus.
2748
2749Note: We need to look at a character but also at the first one in a sub list
2750and there we ignore leading kerns and glue. Elsewhere is code that removes
2751kerns assuming that is italic correction. The heuristics are unreliable for
2752the new fonts so eventualy there will be an option to ignore such corrections.
2753
2754@ @c
2755#define analyze_script(init,su_n,su_f,su_c) do {                                   \
2756      su_n = init;                                                                 \
2757      if (su_n != null) {                                                          \
2758          if (type(su_n) == sub_mlist_node) {                                      \
2759              su_n = math_list(su_n);                                              \
2760              if (su_n != null) {                                                  \
2761                  while (su_n) {                                                   \
2762                      if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) {\
2763                          su_n = vlink(su_n);                                      \
2764                      } else if (type(su_n) == simple_noad) {                      \
2765                          su_n = nucleus(su_n);                                    \
2766                          if (type(su_n) != math_char_node) {                      \
2767                              su_n = null;                                         \
2768                          }                                                        \
2769                          break;                                                   \
2770                      } else {                                                     \
2771                          su_n = null;                                             \
2772                          break;                                                   \
2773                      }                                                            \
2774                  }                                                                \
2775              }                                                                    \
2776          }                                                                        \
2777          if (su_n != null) {                                                      \
2778              fetch(su_n);                                                         \
2779              if (char_exists(cur_f, cur_c)) {                                     \
2780                  su_f = cur_f;                                                    \
2781                  su_c = cur_c;                                                    \
2782              } else {                                                             \
2783                  su_n = null;                                                     \
2784              }                                                                    \
2785          }                                                                        \
2786      }                                                                            \
2787  } while (0)
2788
2789
2790static void make_scripts(pointer q, pointer p, scaled it, int cur_style)
2791{
2792    pointer x, y, z;            /* temporary registers for box construction */
2793    scaled shift_up, shift_down, clr;   /* dimensions in the calculation */
2794    scaled delta1, delta2;
2795    halfword sub_n, sup_n;
2796    internal_font_number sub_f, sup_f;
2797    int sub_c, sup_c;
2798    sub_n = null;
2799    sup_n = null;
2800    sub_f = 0;
2801    sup_f = 0;
2802    sub_c = 0;
2803    sup_c = 0;
2804    delta1 = it;
2805    delta2 = 0;
2806
2807#ifdef DEBUG
2808    printf("it: %d\n", it);
2809    dump_simple_node(q);
2810    printf("p: node %d, type=%d, subtype=%d\n", p, type(p), subtype(p));
2811#endif
2812    switch (type(nucleus(q))) {
2813    case math_char_node:
2814    case math_text_char_node:
2815        if ((subscr(q) == null) && (delta1 != 0)) {
2816            x = new_kern(delta1);
2817            reset_attributes(x, node_attr(nucleus(q)));
2818            couple_nodes(p,x);
2819            delta1 = 0;
2820        }
2821    }
2822    assign_new_hlist(q, p);
2823    if (is_char_node(p)) {
2824        shift_up = 0;
2825        shift_down = 0;
2826    } else {
2827        z = hpack(p, 0, additional, -1);
2828        shift_up = height(z) - sup_shift_drop(cur_style);       /* r18 */
2829        shift_down = depth(z) + sub_shift_drop(cur_style);      /* r19 */
2830        list_ptr(z) = null;
2831        flush_node(z);
2832    }
2833
2834    if (is_char_node(p)) {
2835        /* we look at the subscript character (_i) or first character in a list (_{ij}) */
2836        analyze_script(subscr(q),sub_n,sub_f,sub_c);
2837        /* we look at the superscript character (^i) or first character in a list (^{ij}) */
2838        analyze_script(supscr(q),sup_n,sup_f,sup_c);
2839    }
2840
2841    if (supscr(q) == null) {
2842
2843        /* Construct a subscript box |x| when there is no superscript */
2844        /* When there is a subscript without a superscript, the top of the subscript
2845           should not exceed the baseline plus four-fifths of the x-height. */
2846        x = clean_box(subscr(q), sub_style(cur_style), cur_style);
2847        width(x) = width(x) + space_after_script(cur_style);
2848        if (shift_down < sub_shift_down(cur_style))
2849            shift_down = sub_shift_down(cur_style);
2850        clr = height(x) - sub_top_max(cur_style);
2851        if (shift_down < clr)
2852            shift_down = clr;
2853        shift_amount(x) = shift_down;
2854
2855        /* now find and correct for horizontal shift */
2856        if (sub_n != null) {
2857            delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
2858            if (delta2 != MATH_KERN_NOT_FOUND && delta2 != 0) {
2859                p = attach_hkern_to_new_hlist(q, delta2);
2860            }
2861        }
2862
2863    } else {
2864        /* Construct a superscript box |x| */
2865        /*The bottom of a superscript should never descend below the baseline plus
2866           one-fourth of the x-height. */
2867        x = clean_box(supscr(q), sup_style(cur_style), cur_style);
2868        width(x) = width(x) + space_after_script(cur_style);
2869        clr = sup_shift_up(cur_style);
2870        if (shift_up < clr)
2871            shift_up = clr;
2872        clr = depth(x) + sup_bottom_min(cur_style);
2873        if (shift_up < clr)
2874            shift_up = clr;
2875
2876        if (subscr(q) == null) {
2877            shift_amount(x) = -shift_up;
2878            /* now find and correct for horizontal shift */
2879            if (sup_n != null) {
2880                clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
2881                if (clr != MATH_KERN_NOT_FOUND && clr != 0) {
2882                    p = attach_hkern_to_new_hlist(q, clr);
2883                }
2884            }
2885        } else {
2886            /* Construct a sub/superscript combination box |x|, with the
2887               superscript offset by |delta| */
2888            /* When both subscript and superscript are present, the subscript must be
2889               separated from the superscript by at least four times |default_rule_thickness|.
2890               If this condition would be violated, the subscript moves down, after which
2891               both subscript and superscript move up so that the bottom of the superscript
2892               is at least as high as the baseline plus four-fifths of the x-height. */
2893
2894            y = clean_box(subscr(q), sub_style(cur_style), cur_style);
2895            width(y) = width(y) + space_after_script(cur_style);
2896            if (shift_down < sub_sup_shift_down(cur_style))
2897                shift_down = sub_sup_shift_down(cur_style);
2898                clr = subsup_vgap(cur_style) - ((shift_up - depth(x)) - (height(y) - shift_down));
2899            if (clr > 0) {
2900                shift_down = shift_down + clr;
2901                clr = sup_sub_bottom_max(cur_style) - (shift_up - depth(x));
2902                if (clr > 0) {
2903                    shift_up = shift_up + clr;
2904                    shift_down = shift_down - clr;
2905                }
2906            }
2907            /* now find and correct for horizontal shift */
2908            if (sub_n != null) {
2909                delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
2910                if (delta2 != MATH_KERN_NOT_FOUND && delta2 != 0) {
2911                    p = attach_hkern_to_new_hlist(q, delta2);
2912                }
2913            }
2914            /* now the horizontal shift for the superscript. */
2915            /* the superscript is also to be shifted by |delta1| (the italic correction) */
2916            clr = MATH_KERN_NOT_FOUND;
2917            if (sup_n != null) {
2918                clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
2919            }
2920
2921            if (delta2 == MATH_KERN_NOT_FOUND)
2922                delta2 = 0;
2923            if (clr != MATH_KERN_NOT_FOUND) {
2924                shift_amount(x) = clr + delta1 - delta2;
2925            } else {
2926                shift_amount(x) = delta1 - delta2;
2927            }
2928            p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
2929            reset_attributes(p, node_attr(q));
2930            couple_nodes(x,p);
2931            couple_nodes(p,y);
2932            x = vpackage(x, 0, additional, max_dimen, math_direction);
2933            reset_attributes(x, node_attr(q));
2934            shift_amount(x) = shift_down;
2935        }
2936    }
2937
2938
2939    if (new_hlist(q) == null) {
2940        new_hlist(q) = x;
2941    } else {
2942        p = new_hlist(q);
2943        while (vlink(p) != null)
2944            p = vlink(p);
2945        couple_nodes(p,x);
2946    }
2947    if (subscr(q) != null) {
2948        math_list(subscr(q)) = null;
2949        flush_node(subscr(q));
2950        subscr(q) = null;
2951    }
2952    if (supscr(q) != null) {
2953        math_list(supscr(q)) = null;
2954        flush_node(supscr(q));
2955        supscr(q) = null;
2956    }
2957
2958}
2959
2960@ The |make_left_right| function constructs a left or right delimiter of
2961the required size and returns the value |open_noad| or |close_noad|. The
2962|left_noad_side| and |right_noad_side| will both be based on the original |style|,
2963so they will have consistent sizes.
2964
2965@c
2966static small_number make_left_right(pointer q, int style, scaled max_d,
2967                                    scaled max_hv)
2968{
2969    scaled delta, delta1, delta2;       /* dimensions used in the calculation */
2970    pointer tmp;
2971    setup_cur_size(style);
2972    delta2 = max_d + math_axis(cur_size);
2973    delta1 = max_hv + max_d - delta2;
2974    if (delta2 > delta1)
2975        delta1 = delta2;        /* |delta1| is max distance from axis */
2976    delta = (delta1 / 500) * delimiter_factor;
2977    delta2 = delta1 + delta1 - delimiter_shortfall;
2978    if (delta < delta2)
2979        delta = delta2;
2980    tmp = var_delimiter(delimiter(q), cur_size, delta, NULL, style);
2981    delimiter(q) = null;
2982    assign_new_hlist(q, tmp);
2983    if (subtype(q) == left_noad_side)
2984        return open_noad_type;
2985    else
2986        return close_noad_type;
2987}
2988
2989@ @c
2990#define TEXT_STYLES(A,B) do {					\
2991    def_math_param(A,display_style,(B),level_one);		\
2992    def_math_param(A,cramped_display_style,(B),level_one);	\
2993    def_math_param(A,text_style,(B),level_one);			\
2994    def_math_param(A,cramped_text_style,(B),level_one);		\
2995  } while (0)
2996
2997#define SCRIPT_STYLES(A,B) do {						\
2998    def_math_param(A,script_style,(B),level_one);			\
2999    def_math_param(A,cramped_script_style,(B),level_one);		\
3000    def_math_param(A,script_script_style,(B),level_one);		\
3001    def_math_param(A,cramped_script_script_style,(B),level_one);	\
3002  } while (0)
3003
3004#define ALL_STYLES(A,B) do {			\
3005    TEXT_STYLES(A,(B));				\
3006    SCRIPT_STYLES(A,(B));			\
3007  } while (0)
3008
3009#define SPLIT_STYLES(A,B,C) do {		\
3010    TEXT_STYLES(A,(B));				\
3011    SCRIPT_STYLES(A,(C));			\
3012  } while (0)
3013
3014
3015void initialize_math_spacing(void)
3016{
3017    /* *INDENT-OFF* */
3018    ALL_STYLES   (math_param_ord_ord_spacing,     0);
3019    ALL_STYLES   (math_param_ord_op_spacing,      thin_mu_skip_code);
3020    SPLIT_STYLES (math_param_ord_bin_spacing,     med_mu_skip_code, 0);
3021    SPLIT_STYLES (math_param_ord_rel_spacing,     thick_mu_skip_code, 0);
3022    ALL_STYLES   (math_param_ord_open_spacing,    0);
3023    ALL_STYLES   (math_param_ord_close_spacing,   0);
3024    ALL_STYLES   (math_param_ord_punct_spacing,   0);
3025    SPLIT_STYLES (math_param_ord_inner_spacing,   thin_mu_skip_code, 0);
3026
3027    ALL_STYLES   (math_param_op_ord_spacing,      thin_mu_skip_code);
3028    ALL_STYLES   (math_param_op_op_spacing,       thin_mu_skip_code);
3029    ALL_STYLES   (math_param_op_bin_spacing,      0);
3030    SPLIT_STYLES (math_param_op_rel_spacing,      thick_mu_skip_code, 0);
3031    ALL_STYLES   (math_param_op_open_spacing,     0);
3032    ALL_STYLES   (math_param_op_close_spacing,    0);
3033    ALL_STYLES   (math_param_op_punct_spacing,    0);
3034    SPLIT_STYLES (math_param_op_inner_spacing,    thin_mu_skip_code, 0);
3035
3036    SPLIT_STYLES (math_param_bin_ord_spacing,     med_mu_skip_code, 0);
3037    SPLIT_STYLES (math_param_bin_op_spacing,      med_mu_skip_code, 0);
3038    ALL_STYLES   (math_param_bin_bin_spacing,     0);
3039    ALL_STYLES   (math_param_bin_rel_spacing,     0);
3040    SPLIT_STYLES (math_param_bin_open_spacing,    med_mu_skip_code, 0);
3041    ALL_STYLES   (math_param_bin_close_spacing,   0);
3042    ALL_STYLES   (math_param_bin_punct_spacing,   0);
3043    SPLIT_STYLES (math_param_bin_inner_spacing,   med_mu_skip_code, 0);
3044
3045    SPLIT_STYLES (math_param_rel_ord_spacing,     thick_mu_skip_code, 0);
3046    SPLIT_STYLES (math_param_rel_op_spacing,      thick_mu_skip_code, 0);
3047    ALL_STYLES   (math_param_rel_bin_spacing,     0);
3048    ALL_STYLES   (math_param_rel_rel_spacing,     0);
3049    SPLIT_STYLES (math_param_rel_open_spacing,    thick_mu_skip_code, 0);
3050    ALL_STYLES   (math_param_rel_close_spacing,   0);
3051    ALL_STYLES   (math_param_rel_punct_spacing,   0);
3052    SPLIT_STYLES (math_param_rel_inner_spacing,   thick_mu_skip_code, 0);
3053
3054    ALL_STYLES   (math_param_open_ord_spacing,    0);
3055    ALL_STYLES   (math_param_open_op_spacing,     0);
3056    ALL_STYLES   (math_param_open_bin_spacing,    0);
3057    ALL_STYLES   (math_param_open_rel_spacing,    0);
3058    ALL_STYLES   (math_param_open_open_spacing,   0);
3059    ALL_STYLES   (math_param_open_close_spacing,  0);
3060    ALL_STYLES   (math_param_open_punct_spacing,  0);
3061    ALL_STYLES   (math_param_open_inner_spacing,  0);
3062
3063    ALL_STYLES   (math_param_close_ord_spacing,   0);
3064    ALL_STYLES   (math_param_close_op_spacing,    thin_mu_skip_code);
3065    SPLIT_STYLES (math_param_close_bin_spacing,   med_mu_skip_code, 0);
3066    SPLIT_STYLES (math_param_close_rel_spacing,   thick_mu_skip_code, 0);
3067    ALL_STYLES   (math_param_close_open_spacing,  0);
3068    ALL_STYLES   (math_param_close_close_spacing, 0);
3069    ALL_STYLES   (math_param_close_punct_spacing, 0);
3070    SPLIT_STYLES (math_param_close_inner_spacing, thin_mu_skip_code, 0);
3071
3072    SPLIT_STYLES (math_param_punct_ord_spacing,   thin_mu_skip_code, 0);
3073    SPLIT_STYLES (math_param_punct_op_spacing,    thin_mu_skip_code, 0);
3074    ALL_STYLES   (math_param_punct_bin_spacing,   0);
3075    SPLIT_STYLES (math_param_punct_rel_spacing,   thin_mu_skip_code, 0);
3076    SPLIT_STYLES (math_param_punct_open_spacing,  thin_mu_skip_code, 0);
3077    SPLIT_STYLES (math_param_punct_close_spacing, thin_mu_skip_code, 0);
3078    SPLIT_STYLES (math_param_punct_punct_spacing, thin_mu_skip_code, 0);
3079    SPLIT_STYLES (math_param_punct_inner_spacing, thin_mu_skip_code, 0);
3080
3081    SPLIT_STYLES (math_param_inner_ord_spacing,   thin_mu_skip_code, 0);
3082    ALL_STYLES   (math_param_inner_op_spacing,    thin_mu_skip_code);
3083    SPLIT_STYLES (math_param_inner_bin_spacing,   med_mu_skip_code, 0);
3084    SPLIT_STYLES (math_param_inner_rel_spacing,   thick_mu_skip_code, 0);
3085    SPLIT_STYLES (math_param_inner_open_spacing,  thin_mu_skip_code, 0);
3086    ALL_STYLES   (math_param_inner_close_spacing, 0);
3087    SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip_code, 0);
3088    SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip_code, 0);
3089    /* *INDENT-ON* */
3090}
3091
3092
3093@ @c
3094#define both_types(A,B) ((A)*16+(B))
3095
3096static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu)
3097{
3098    int x = -1;
3099    pointer z = null;
3100    if (l_type == op_noad_type_limits || l_type == op_noad_type_no_limits)
3101        l_type = op_noad_type_normal;
3102    if (r_type == op_noad_type_limits || r_type == op_noad_type_no_limits)
3103        r_type = op_noad_type_normal;
3104    switch (both_types(l_type, r_type)) {
3105    /* *INDENT-OFF* */
3106    case both_types(ord_noad_type,  ord_noad_type  ):  x = get_math_param(math_param_ord_ord_spacing,mstyle); break;
3107    case both_types(ord_noad_type,  op_noad_type_normal   ):  x = get_math_param(math_param_ord_op_spacing,mstyle); break;
3108    case both_types(ord_noad_type,  bin_noad_type  ):  x = get_math_param(math_param_ord_bin_spacing,mstyle); break;
3109    case both_types(ord_noad_type,  rel_noad_type  ):  x = get_math_param(math_param_ord_rel_spacing,mstyle); break;
3110    case both_types(ord_noad_type,  open_noad_type ):  x = get_math_param(math_param_ord_open_spacing,mstyle); break;
3111    case both_types(ord_noad_type,  close_noad_type):  x = get_math_param(math_param_ord_close_spacing,mstyle); break;
3112    case both_types(ord_noad_type,  punct_noad_type):  x = get_math_param(math_param_ord_punct_spacing,mstyle); break;
3113    case both_types(ord_noad_type,  inner_noad_type):  x = get_math_param(math_param_ord_inner_spacing,mstyle); break;
3114    case both_types(op_noad_type_normal, ord_noad_type  ):  x = get_math_param(math_param_op_ord_spacing,mstyle); break;
3115    case both_types(op_noad_type_normal, op_noad_type_normal   ):  x = get_math_param(math_param_op_op_spacing,mstyle); break;
3116#if 0
3117      case both_types(op_noad_type_normal,   bin_noad_type  ):  x = get_math_param(math_param_op_bin_spacing,mstyle); break;
3118#endif
3119    case both_types(op_noad_type_normal,   rel_noad_type  ):  x = get_math_param(math_param_op_rel_spacing,mstyle); break;
3120    case both_types(op_noad_type_normal,   open_noad_type ):  x = get_math_param(math_param_op_open_spacing,mstyle); break;
3121    case both_types(op_noad_type_normal,   close_noad_type):  x = get_math_param(math_param_op_close_spacing,mstyle); break;
3122    case both_types(op_noad_type_normal,   punct_noad_type):  x = get_math_param(math_param_op_punct_spacing,mstyle); break;
3123    case both_types(op_noad_type_normal,   inner_noad_type):  x = get_math_param(math_param_op_inner_spacing,mstyle); break;
3124    case both_types(bin_noad_type,  ord_noad_type  ):  x = get_math_param(math_param_bin_ord_spacing,mstyle); break;
3125    case both_types(bin_noad_type,  op_noad_type_normal   ):  x = get_math_param(math_param_bin_op_spacing,mstyle); break;
3126#if 0
3127      case both_types(bin_noad_type,  bin_noad_type  ):  x = get_math_param(math_param_bin_bin_spacing,mstyle); break;
3128      case both_types(bin_noad_type,  rel_noad_type  ):  x = get_math_param(math_param_bin_rel_spacing,mstyle); break;
3129#endif
3130    case both_types(bin_noad_type,  open_noad_type ):  x = get_math_param(math_param_bin_open_spacing,mstyle); break;
3131#if 0
3132      case both_types(bin_noad_type,  close_noad_type):  x = get_math_param(math_param_bin_close_spacing,mstyle); break;
3133      case both_types(bin_noad_type,  punct_noad_type):  x = get_math_param(math_param_bin_punct_spacing,mstyle); break;
3134#endif
3135    case both_types(bin_noad_type,  inner_noad_type):  x = get_math_param(math_param_bin_inner_spacing,mstyle); break;
3136    case both_types(rel_noad_type,  ord_noad_type  ):  x = get_math_param(math_param_rel_ord_spacing,mstyle); break;
3137    case both_types(rel_noad_type,  op_noad_type_normal   ):  x = get_math_param(math_param_rel_op_spacing,mstyle); break;
3138#if 0
3139      case both_types(rel_noad_type,  bin_noad_type  ):  x = get_math_param(math_param_rel_bin_spacing,mstyle); break;
3140#endif
3141    case both_types(rel_noad_type,  rel_noad_type  ):  x = get_math_param(math_param_rel_rel_spacing,mstyle); break;
3142    case both_types(rel_noad_type,  open_noad_type ):  x = get_math_param(math_param_rel_open_spacing,mstyle); break;
3143    case both_types(rel_noad_type,  close_noad_type):  x = get_math_param(math_param_rel_close_spacing,mstyle); break;
3144    case both_types(rel_noad_type,  punct_noad_type):  x = get_math_param(math_param_rel_punct_spacing,mstyle); break;
3145    case both_types(rel_noad_type,  inner_noad_type):  x = get_math_param(math_param_rel_inner_spacing,mstyle); break;
3146    case both_types(open_noad_type, ord_noad_type  ):  x = get_math_param(math_param_open_ord_spacing,mstyle); break;
3147    case both_types(open_noad_type, op_noad_type_normal   ):  x = get_math_param(math_param_open_op_spacing,mstyle); break;
3148#if 0
3149      case both_types(open_noad_type, bin_noad_type  ):  x = get_math_param(math_param_open_bin_spacing,mstyle); break;
3150#endif
3151    case both_types(open_noad_type, rel_noad_type  ):  x = get_math_param(math_param_open_rel_spacing,mstyle); break;
3152    case both_types(open_noad_type, open_noad_type ):  x = get_math_param(math_param_open_open_spacing,mstyle); break;
3153    case both_types(open_noad_type, close_noad_type):  x = get_math_param(math_param_open_close_spacing,mstyle); break;
3154    case both_types(open_noad_type, punct_noad_type):  x = get_math_param(math_param_open_punct_spacing,mstyle); break;
3155    case both_types(open_noad_type, inner_noad_type):  x = get_math_param(math_param_open_inner_spacing,mstyle); break;
3156    case both_types(close_noad_type,ord_noad_type  ):  x = get_math_param(math_param_close_ord_spacing,mstyle); break;
3157    case both_types(close_noad_type,op_noad_type_normal   ):  x = get_math_param(math_param_close_op_spacing,mstyle); break;
3158    case both_types(close_noad_type,bin_noad_type  ):  x = get_math_param(math_param_close_bin_spacing,mstyle); break;
3159    case both_types(close_noad_type,rel_noad_type  ):  x = get_math_param(math_param_close_rel_spacing,mstyle); break;
3160    case both_types(close_noad_type,open_noad_type ):  x = get_math_param(math_param_close_open_spacing,mstyle); break;
3161    case both_types(close_noad_type,close_noad_type):  x = get_math_param(math_param_close_close_spacing,mstyle); break;
3162    case both_types(close_noad_type,punct_noad_type):  x = get_math_param(math_param_close_punct_spacing,mstyle); break;
3163    case both_types(close_noad_type,inner_noad_type):  x = get_math_param(math_param_close_inner_spacing,mstyle); break;
3164    case both_types(punct_noad_type,ord_noad_type  ):  x = get_math_param(math_param_punct_ord_spacing,mstyle); break;
3165    case both_types(punct_noad_type,op_noad_type_normal   ):  x = get_math_param(math_param_punct_op_spacing,mstyle); break;
3166#if 0
3167      case both_types(punct_noad_type,bin_noad_type  ):  x = get_math_param(math_param_punct_bin_spacing,mstyle); break;
3168#endif
3169    case both_types(punct_noad_type,rel_noad_type  ):  x = get_math_param(math_param_punct_rel_spacing,mstyle); break;
3170    case both_types(punct_noad_type,open_noad_type ):  x = get_math_param(math_param_punct_open_spacing,mstyle); break;
3171    case both_types(punct_noad_type,close_noad_type):  x = get_math_param(math_param_punct_close_spacing,mstyle); break;
3172    case both_types(punct_noad_type,punct_noad_type):  x = get_math_param(math_param_punct_punct_spacing,mstyle); break;
3173    case both_types(punct_noad_type,inner_noad_type):  x = get_math_param(math_param_punct_inner_spacing,mstyle); break;
3174    case both_types(inner_noad_type,ord_noad_type  ):  x = get_math_param(math_param_inner_ord_spacing,mstyle); break;
3175    case both_types(inner_noad_type,op_noad_type_normal   ):  x = get_math_param(math_param_inner_op_spacing,mstyle); break;
3176    case both_types(inner_noad_type,bin_noad_type  ):  x = get_math_param(math_param_inner_bin_spacing,mstyle); break;
3177    case both_types(inner_noad_type,rel_noad_type  ):  x = get_math_param(math_param_inner_rel_spacing,mstyle); break;
3178    case both_types(inner_noad_type,open_noad_type ):  x = get_math_param(math_param_inner_open_spacing,mstyle); break;
3179    case both_types(inner_noad_type,close_noad_type):  x = get_math_param(math_param_inner_close_spacing,mstyle); break;
3180    case both_types(inner_noad_type,punct_noad_type):  x = get_math_param(math_param_inner_punct_spacing,mstyle); break;
3181    case both_types(inner_noad_type,inner_noad_type):  x = get_math_param(math_param_inner_inner_spacing,mstyle); break;
3182    /* *INDENT-ON* */
3183    }
3184    if (x < 0) {
3185        confusion("mathspacing");
3186    }
3187    if (x != 0) {
3188        pointer y;
3189        if (x <= thick_mu_skip_code) {  /* trap thin/med/thick settings cf. old TeX */
3190            y = math_glue(glue_par(x), mmu);
3191            z = new_glue(y);
3192            glue_ref_count(y) = null;
3193            subtype(z) = (quarterword) (x + 1); /* store a symbolic subtype */
3194        } else {
3195            y = math_glue(x, mmu);
3196            z = new_glue(y);
3197            glue_ref_count(y) = null;
3198        }
3199    }
3200    return z;
3201}
3202
3203@ @c
3204static pointer check_nucleus_complexity(halfword q, scaled * delta,
3205                                        int cur_style)
3206{
3207    pointer p = null;
3208    switch (type(nucleus(q))) {
3209    case math_char_node:
3210    case math_text_char_node:
3211        fetch(nucleus(q));
3212        if (char_exists(cur_f, cur_c)) {
3213            *delta = char_italic(cur_f, cur_c);
3214            p = new_glyph(cur_f, cur_c);
3215            reset_attributes(p, node_attr(nucleus(q)));
3216            if ((is_new_mathfont(cur_f) && get_char_cat_code(cur_c) == 11) ||
3217                (!is_new_mathfont(cur_f) && type(nucleus(q)) == math_text_char_node && space(cur_f)) != 0) {
3218                *delta = 0;     /* no italic correction in mid-word of text font */
3219	    }
3220            if ((subscr(q) == null) && (supscr(q) == null) && (*delta != 0)) {
3221                pointer x = new_kern(*delta);
3222                reset_attributes(x, node_attr(nucleus(q)));
3223                couple_nodes(p,x);
3224                *delta = 0;
3225            }
3226        }
3227        break;
3228    case sub_box_node:
3229        p = math_list(nucleus(q));
3230        break;
3231    case sub_mlist_node:
3232        mlist_to_hlist_args(math_list(nucleus(q)), cur_style, false);   /* recursive call */
3233        setup_cur_size(cur_style);
3234        p = hpack(vlink(temp_head), 0, additional, -1);
3235        reset_attributes(p, node_attr(nucleus(q)));
3236        break;
3237    default:
3238        confusion("mlist2");    /* this can't happen mlist2 */
3239    }
3240    return p;
3241}
3242
3243@ Here is the overall plan of |mlist_to_hlist|, and the list of its
3244   local variables.
3245
3246@c
3247static void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
3248{
3249    pointer q;                  /* runs through the mlist */
3250    pointer r;                  /* the most recent noad preceding |q| */
3251    int style;
3252    int r_type;                 /* the |type| of noad |r|, or |op_noad| if |r=null| */
3253    int r_subtype;              /* the |subtype| of noad |r| if |r_type| is |fence_noad| */
3254    int t;                      /* the effective |type| of noad |q| during the second pass */
3255    int t_subtype;              /* the effective |subtype| of noad |q| during the second pass */
3256    pointer p, x, y, z;         /* temporary registers for list construction */
3257    int pen;                    /* a penalty to be inserted */
3258    scaled max_hl, max_d;       /* maximum height and depth of the list translated so far */
3259    scaled delta;               /* italic correction offset for subscript and superscript */
3260    scaled cur_mu;              /* the math unit width corresponding to |cur_size| */
3261    style = cur_style;          /* tuck global parameter away as local variable */
3262    q = mlist;
3263    r = null;
3264    r_type = simple_noad;
3265    r_subtype = op_noad_type_normal;
3266    max_hl = 0;
3267    max_d = 0;
3268    x = null;
3269    p = null;
3270    setup_cur_size(cur_style);
3271    cur_mu = x_over_n(get_math_quad(cur_size), 18);
3272    while (q != null) {
3273        /* We use the fact that no character nodes appear in an mlist, hence
3274           the field |type(q)| is always present. */
3275
3276        /* One of the things we must do on the first pass is change a |bin_noad| to
3277           an |ord_noad| if the |bin_noad| is not in the context of a binary operator.
3278           The values of |r| and |r_type| make this fairly easy. */
3279      RESWITCH:
3280        delta = 0;
3281        switch (type(q)) {
3282        case simple_noad:
3283            switch (subtype(q)) {
3284            case bin_noad_type:
3285                switch (r_type) {
3286                case simple_noad:
3287                    switch (r_subtype) {
3288                    case bin_noad_type:
3289                    case op_noad_type_normal:
3290                    case op_noad_type_limits:
3291                    case op_noad_type_no_limits:
3292                    case rel_noad_type:
3293                    case open_noad_type:
3294                    case punct_noad_type:
3295                        subtype(q) = ord_noad_type;
3296                        goto RESWITCH;
3297                        break;
3298                    }
3299                    break;
3300                case fence_noad:
3301                    if (r_subtype == left_noad_side) {
3302                        subtype(q) = ord_noad_type;
3303                        goto RESWITCH;
3304                    }
3305                    break;
3306                }
3307                break;
3308            case over_noad_type:
3309                make_over(q, cur_style);
3310                break;
3311            case under_noad_type:
3312                make_under(q, cur_style);
3313                break;
3314            case vcenter_noad_type:
3315                make_vcenter(q);
3316                break;
3317            case rel_noad_type:
3318            case close_noad_type:
3319            case punct_noad_type:
3320                if (r_type == simple_noad && r_subtype == bin_noad_type) {
3321                    type(r) = simple_noad;
3322                    subtype(r) = ord_noad_type;
3323                }
3324                break;
3325            case op_noad_type_normal:
3326            case op_noad_type_limits:
3327            case op_noad_type_no_limits:
3328                delta = make_op(q, cur_style);
3329                if (subtype(q) == op_noad_type_limits)
3330                    goto CHECK_DIMENSIONS;
3331                break;
3332            case ord_noad_type:
3333                make_ord(q);
3334                break;
3335            case open_noad_type:
3336            case inner_noad_type:
3337                break;
3338            }
3339            break;
3340        case fence_noad:
3341            if (subtype(q) != left_noad_side)
3342                if (r_type == simple_noad && r_subtype == bin_noad_type) {
3343                    type(r) = simple_noad;
3344                    subtype(r) = ord_noad_type;
3345                }
3346            goto DONE_WITH_NOAD;
3347            break;
3348        case fraction_noad:
3349            make_fraction(q, cur_style);
3350            goto CHECK_DIMENSIONS;
3351            break;
3352        case radical_noad:
3353            if (subtype(q) == 4)
3354                make_under_delimiter(q, cur_style);
3355            else if (subtype(q) == 5)
3356                make_over_delimiter(q, cur_style);
3357            else if (subtype(q) == 6)
3358                make_delimiter_under(q, cur_style);
3359            else if (subtype(q) == 7)
3360                make_delimiter_over(q, cur_style);
3361            else
3362                make_radical(q, cur_style);
3363            break;
3364        case accent_noad:
3365          make_math_accent(q, cur_style);
3366            break;
3367        case style_node:
3368            cur_style = subtype(q);
3369            setup_cur_size(cur_style);
3370            /* HH-LS: was cur_mu = x_over_n(get_math_quad(cur_size), 18);*/
3371            /* This is an old bug so the fix can influence outcome       */
3372            cur_mu = x_over_n(get_math_quad(cur_style), 18);
3373            goto DONE_WITH_NODE;
3374            break;
3375        case choice_node:
3376            switch (cur_style / 2) {
3377            case 0:
3378                choose_mlist(display_mlist);
3379                break;          /* |display_style=0| */
3380            case 1:
3381                choose_mlist(text_mlist);
3382                break;          /* |text_style=2| */
3383            case 2:
3384                choose_mlist(script_mlist);
3385                break;          /* |script_style=4| */
3386            case 3:
3387                choose_mlist(script_script_mlist);
3388                break;          /* |script_script_style=6| */
3389            }                   /* there are no other cases */
3390            flush_node_list(display_mlist(q));
3391            flush_node_list(text_mlist(q));
3392            flush_node_list(script_mlist(q));
3393            flush_node_list(script_script_mlist(q));
3394            type(q) = style_node;
3395            subtype(q) = (quarterword) cur_style;
3396            if (p != null) {
3397                z = vlink(q);
3398                couple_nodes(q,p);
3399                while (vlink(p) != null)
3400                    p = vlink(p);
3401                try_couple_nodes(p,z);
3402            }
3403            goto DONE_WITH_NODE;
3404            break;
3405        case ins_node:
3406        case mark_node:
3407        case adjust_node:
3408        case whatsit_node:
3409        case penalty_node:
3410        case disc_node:
3411            goto DONE_WITH_NODE;
3412            break;
3413        case rule_node:
3414            if (height(q) > max_hl)
3415                max_hl = height(q);
3416            if (depth(q) > max_d)
3417                max_d = depth(q);
3418            goto DONE_WITH_NODE;
3419            break;
3420        case glue_node:
3421            /*
3422               Conditional math glue (`\.{\\nonscript}') results in a |glue_node|
3423               pointing to |zero_glue|, with |subtype(q)=cond_math_glue|; in such a case
3424               the node following will be eliminated if it is a glue or kern node and if the
3425               current size is different from |text_size|. Unconditional math glue
3426               (`\.{\\muskip}') is converted to normal glue by multiplying the dimensions
3427               by |cur_mu|.
3428             */
3429            if (subtype(q) == mu_glue) {
3430                x = glue_ptr(q);
3431                y = math_glue(x, cur_mu);
3432                delete_glue_ref(x);
3433                glue_ptr(q) = y;
3434                subtype(q) = normal;
3435            } else if ((cur_size != text_size)
3436                       && (subtype(q) == cond_math_glue)) {
3437                p = vlink(q);
3438                if (p != null)
3439                    if ((type(p) == glue_node) || (type(p) == kern_node)) {
3440                        couple_nodes(q,vlink(p));
3441                        vlink(p) = null;
3442                        flush_node_list(p);
3443                    }
3444            }
3445            goto DONE_WITH_NODE;
3446            break;
3447        case kern_node:
3448            math_kern(q, cur_mu);
3449            goto DONE_WITH_NODE;
3450            break;
3451        default:
3452            confusion("mlist1");        /* this can't happen mlist1 */
3453        }
3454        /* When we get to the following part of the program, we have ``fallen through''
3455           from cases that did not lead to |check_dimensions| or |done_with_noad| or
3456           |done_with_node|. Thus, |q|~points to a noad whose nucleus may need to be
3457           converted to an hlist, and whose subscripts and superscripts need to be
3458           appended if they are present.
3459
3460           If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount
3461           by which a superscript should be moved right with respect to a subscript
3462           when both are present.
3463         */
3464        p = check_nucleus_complexity(q, &delta, cur_style);
3465
3466        if ((subscr(q) == null) && (supscr(q) == null)) {
3467            assign_new_hlist(q, p);
3468        } else {
3469            make_scripts(q, p, delta, cur_style);       /* top, bottom */
3470        }
3471      CHECK_DIMENSIONS:
3472        z = hpack(new_hlist(q), 0, additional, -1);
3473        if (height(z) > max_hl)
3474            max_hl = height(z);
3475        if (depth(z) > max_d)
3476            max_d = depth(z);
3477        list_ptr(z) = null;
3478        flush_node(z);          /* only drop the \.{\\hbox} */
3479      DONE_WITH_NOAD:
3480        r = q;
3481        r_type = type(r);
3482        r_subtype = subtype(r);
3483        if (r_type == fence_noad) {
3484            r_subtype = left_noad_side;
3485            cur_style = style;
3486            setup_cur_size(cur_style);
3487            cur_mu = x_over_n(get_math_quad(cur_size), 18);
3488        }
3489      DONE_WITH_NODE:
3490        q = vlink(q);
3491    }
3492    if (r_type == simple_noad && r_subtype == bin_noad_type) {
3493        type(r) = simple_noad;
3494        subtype(r) = ord_noad_type;
3495    }
3496    /* Make a second pass over the mlist, removing all noads and inserting the
3497       proper spacing and penalties */
3498
3499    /* We have now tied up all the loose ends of the first pass of |mlist_to_hlist|.
3500       The second pass simply goes through and hooks everything together with the
3501       proper glue and penalties. It also handles the |fence_noad|s that
3502       might be present, since |max_hl| and |max_d| are now known. Variable |p| points
3503       to a node at the current end of the final hlist.
3504     */
3505    p = temp_head;
3506    vlink(p) = null;
3507    q = mlist;
3508    r_type = 0;
3509    r_subtype = 0;
3510    cur_style = style;
3511    setup_cur_size(cur_style);
3512    cur_mu = x_over_n(get_math_quad(cur_size), 18);
3513  NEXT_NODE:
3514    while (q != null) {
3515        /* If node |q| is a style node, change the style and |goto delete_q|;
3516           otherwise if it is not a noad, put it into the hlist,
3517           advance |q|, and |goto done|; otherwise set |s| to the size
3518           of noad |q|, set |t| to the associated type (|ord_noad..
3519           inner_noad|), and set |pen| to the associated penalty */
3520        /* Just before doing the big |case| switch in the second pass, the program
3521           sets up default values so that most of the branches are short. */
3522        t = simple_noad;
3523        t_subtype = ord_noad_type;
3524        pen = inf_penalty;
3525        switch (type(q)) {
3526        case simple_noad:
3527            t_subtype = subtype(q);
3528            switch (t_subtype) {
3529            case bin_noad_type:
3530                pen = bin_op_penalty;
3531                break;
3532            case rel_noad_type:
3533                pen = rel_penalty;
3534                break;
3535            case vcenter_noad_type:
3536            case over_noad_type:
3537            case under_noad_type:
3538                t_subtype = ord_noad_type;
3539                break;
3540            }
3541        case radical_noad:
3542            break;
3543        case accent_noad:
3544            break;
3545        case fraction_noad:
3546            t = simple_noad;
3547            t_subtype = inner_noad_type;
3548            break;
3549        case fence_noad:
3550            t_subtype = make_left_right(q, style, max_d, max_hl);
3551            break;
3552        case style_node:
3553            /* Change the current style and |goto delete_q| */
3554            cur_style = subtype(q);
3555            setup_cur_size(cur_style);
3556            cur_mu = x_over_n(get_math_quad(cur_size), 18);
3557            goto DELETE_Q;
3558            break;
3559        case whatsit_node:
3560        case penalty_node:
3561        case rule_node:
3562        case disc_node:
3563        case adjust_node:
3564        case ins_node:
3565        case mark_node:
3566        case glue_node:
3567        case kern_node:
3568            couple_nodes(p,q);
3569            p = q;
3570            q = vlink(q);
3571            vlink(p) = null;
3572            goto NEXT_NODE;
3573            break;
3574        default:
3575            confusion("mlist3");        /* this can't happen mlist3 */
3576        }
3577        /* Append inter-element spacing based on |r_type| and |t| */
3578        if (r_type > 0) {       /* not the first noad */
3579            z = math_spacing_glue(r_subtype, t_subtype, cur_style, cur_mu);
3580            if (z != null) {
3581                reset_attributes(z, node_attr(p));
3582                couple_nodes(p,z);
3583                p = z;
3584            }
3585        }
3586
3587        /* Append any |new_hlist| entries for |q|, and any appropriate penalties */
3588        /* We insert a penalty node after the hlist entries of noad |q| if |pen|
3589           is not an ``infinite'' penalty, and if the node immediately following |q|
3590           is not a penalty node or a |rel_noad| or absent entirely. */
3591
3592        if (new_hlist(q) != null) {
3593            couple_nodes(p,new_hlist(q));
3594            do {
3595                p = vlink(p);
3596            } while (vlink(p) != null);
3597        }
3598        if (penalties && vlink(q) != null && pen < inf_penalty) {
3599            r_type = type(vlink(q));
3600            r_subtype = subtype(vlink(q));
3601            if (r_type != penalty_node
3602                && (r_type != simple_noad || r_subtype != rel_noad_type)) {
3603                z = new_penalty(pen);
3604                reset_attributes(z, node_attr(q));
3605                couple_nodes(p,z);
3606                p = z;
3607            }
3608        }
3609        if (type(q) == fence_noad && subtype(q) == right_noad_side) {
3610            t = simple_noad;
3611            t_subtype = open_noad_type;
3612        }
3613        r_type = t;
3614        r_subtype = t_subtype;
3615      DELETE_Q:
3616        r = q;
3617        q = vlink(q);
3618        /* The m-to-hlist conversion takes place in-place,
3619           so the various dependant fields may not be freed
3620           (as would happen if |flush_node| was called).
3621           A low-level |free_node| is easier than attempting
3622           to nullify such dependant fields for all possible
3623           node and noad types.
3624         */
3625        if (nodetype_has_attributes(type(r)))
3626            delete_attribute_ref(node_attr(r));
3627        free_node(r, get_node_size(type(r), subtype(r)));
3628    }
3629}
3630
3631@ This used to be needed when |mlist_to_hlist| communicated via global
3632variables.
3633
3634@c
3635void mlist_to_hlist_args(pointer n, int w, boolean m)
3636{
3637    mlist_to_hlist(n, m, w);
3638}
3639