/* Copyright 2009-2012 Taco Hoekwater This file is part of LuaTeX. LuaTeX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License along with LuaTeX; if not, see . */ #include "ptexlib.h" static void scan_expr(void); /*tex Let's turn now to some procedures that \TeX\ calls upon frequently to digest certain kinds of patterns in the input. Most of these are quite simple; some are quite elaborate. Almost all of the routines call |get_x_token|, which can cause them to be invoked recursively. The |scan_left_brace| routine is called when a left brace is supposed to be the next non-blank token. (The term ``left brace'' means, more precisely, a character whose catcode is |left_brace|.) \TeX\ allows \.{\\relax} to appear before the |left_brace|. */ /* This reads a mandatory |left_brace|: */ void scan_left_brace(void) { /*tex Get the next non-blank non-relax non-call token */ do { get_x_token(); } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); if (cur_cmd != left_brace_cmd) { print_err("Missing { inserted"); help4( "A left brace was mandatory here, so I've put one in.", "You might want to delete and/or insert some corrections", "so that I will find a matching right brace soon.", "If you're confused by all this, try typing `I}' now." ); back_error(); cur_tok = left_brace_token + '{'; cur_cmd = left_brace_cmd; cur_chr = '{'; incr(align_state); } } /*tex The |scan_optional_equals| routine looks for an optional `\.=' sign preceded by optional spaces; `\.{\\relax}' is not ignored here. */ void scan_optional_equals(void) { /*tex Get the next non-blank non-call token */ do { get_x_token(); } while (cur_cmd == spacer_cmd); if (cur_tok != other_token + '=') back_input(); } /*tex Here is a procedure that sounds an alarm when mu and non-mu units are being switched. */ static void mu_error(void) { print_err("Incompatible glue units"); help1("I'm going to assume that 1mu=1pt when they're mixed."); error(); } /*tex The next routine `|scan_something_internal|' is used to fetch internal numeric quantities like `\.{\\hsize}', and also to handle the `\.{\\the}' when expanding constructions like `\.{\\the\\toks0}' and `\.{\\the\\baselineskip}'. Soon we will be considering the |scan_int| procedure, which calls |scan_something_internal|; on the other hand, |scan_something_internal| also calls |scan_int|, for constructions like `\.{\\catcode\`\\\$}' or `\.{\\fontdimen} \.3 \.{\\ff}'. So we have to declare |scan_int| as a |forward| procedure. A few other procedures are also declared at this point. \TeX\ doesn't know exactly what to expect when |scan_something_internal| begins. For example, an integer or dimension or glue value could occur immediately after `\.{\\hskip}'; and one can even say \.{\\the} with respect to token lists in constructions like `\.{\\xdef\\o\{\\the\\output\}}'. On the other hand, only integers are allowed after a construction like `\.{\\count}'. To handle the various possibilities, |scan_something_internal| has a |level| parameter, which tells the ``highest'' kind of quantity that |scan_something_internal| is allowed to produce. Eight levels are distinguished, namely |int_val|, |attr_val|, |dimen_val|, |glue_val|, |mu_val|, |dir_val|, |ident_val|, and |tok_val|. The output of |scan_something_internal| (and of the other routines |scan_int|, |scan_dimen|, and |scan_glue| below) is put into the global variable |cur_val|, and its level is put into |cur_val_level|. The highest values of |cur_val_level| are special: |mu_val| is used only when |cur_val| points to something in a ``muskip'' register, or to one of the three parameters \.{\\thinmuskip}, \.{\\medmuskip}, \.{\\thickmuskip}; |ident_val| is used only when |cur_val| points to a font identifier; |tok_val| is used only when |cur_val| points to |null| or to the reference count of a token list. The last two cases are allowed only when |scan_something_internal| is called with |level=tok_val|. If the output is glue, |cur_val| will point to a glue specification, and the reference count of that glue will have been updated to reflect this reference; if the output is a nonempty token list, |cur_val| will point to its reference count, but in this case the count will not have been updated. Otherwise |cur_val| will contain the integer or scaled value in question. */ /*tex The value returned by numeric scanners: */ int cur_val; /*tex Delcodes are sometimes 51 digits: */ int cur_val1; /*tex The level of this value: */ int cur_val_level; #define scanned_result(A,B) do { \ cur_val=A; \ cur_val_level=B; \ } while (0) /*tex When a |glue_val| changes to a |dimen_val|, we use the width component of the glue; there is no need to decrease the reference count, since it has not yet been increased. When a |dimen_val| changes to an |int_val|, we use scaled points so that the value doesn't actually change. And when a |mu_val| changes to a |glue_val|, the value doesn't change either. */ static void downgrade_cur_val(boolean delete_glue) { if (cur_val_level == glue_val_level) { halfword m = cur_val; cur_val = width(m); if (delete_glue) flush_node(m); } else if (cur_val_level == mu_val_level) { mu_error(); } decr(cur_val_level); } void negate_cur_val(boolean modify_glue) { if (cur_val_level >= glue_val_level) { if (modify_glue) { /*tex We modify in-place. */ } else { cur_val = new_spec(cur_val); } negate(width(cur_val)); negate(stretch(cur_val)); negate(shrink(cur_val)); } else { negate(cur_val); } } /*tex Some of the internal items can be fetched both routines, and these have been split off into the next routine, that returns true if the command code was understood. */ static boolean short_scan_something_internal(int cmd, int chr, int level, boolean negative) { /*tex |chr_code| part of the operand token */ halfword m; /*tex general purpose index */ halfword q; /*tex index into |nest| */ int p; int save_cur_chr; boolean succeeded = true; m = chr; switch (cmd) { case assign_toks_cmd: scanned_result(equiv(m), tok_val_level); break; case assign_int_cmd: scanned_result(eqtb[m].cint, int_val_level); break; case assign_attr_cmd: scanned_result(eqtb[m].cint, int_val_level); break; case assign_dimen_cmd: scanned_result(eqtb[m].cint, dimen_val_level); break; case assign_glue_cmd: scanned_result(equiv(m), glue_val_level); break; case assign_mu_glue_cmd: scanned_result(equiv(m), mu_val_level); break; case assign_direction_cmd: if (m == (int_base + line_direction_code)) { m = int_base + text_direction_code; } scanned_result(eqtb[m].cint, int_val_level); break; case assign_dir_cmd: if (m == (int_base + line_direction_code)) { m = int_base + text_direction_code; } scanned_result(eqtb[m].cint, dir_val_level); break; case math_style_cmd: scanned_result(m, int_val_level); break; case set_aux_cmd: /*tex Fetch the |space_factor| or the |prev_depth|. */ if (abs(cur_list.mode_field) != m) { print_err("Improper "); print_cmd_chr(set_aux_cmd, m); help4( "You can refer to \\spacefactor only in horizontal mode;", "you can refer to \\prevdepth only in vertical mode; and", "neither of these is meaningful inside \\write. So", "I'm forgetting what you said and using zero instead." ); error(); if (level != tok_val_level) scanned_result(0, dimen_val_level); else scanned_result(0, int_val_level); } else if (m == vmode) { scanned_result(prev_depth_par, dimen_val_level); } else { scanned_result(space_factor_par, int_val_level); } break; case set_prev_graf_cmd: /*tex Fetch the |prev_graf| */ if (cur_list.mode_field == 0) { /*tex |prev_graf=0| within \.{\\write} */ scanned_result(0, int_val_level); } else { p = nest_ptr; while (abs(nest[p].mode_field) != vmode) decr(p); scanned_result(nest[p].pg_field, int_val_level); } break; case set_page_int_cmd: /*tex Fetch the |dead_cycles| or the |insert_penalties| */ if (m == 0) cur_val = dead_cycles; else if (m == 2) cur_val = interaction; else cur_val = insert_penalties; cur_val_level = int_val_level; break; case set_page_dimen_cmd: /*tex Fetch something on the |page_so_far|. */ if ((page_contents == empty) && (!output_active)) { if (m == 0) cur_val = max_dimen; else cur_val = 0; } else { cur_val = page_so_far[m]; } cur_val_level = dimen_val_level; break; case set_tex_shape_cmd: /*tex Fetch the |par_shape| size. */ if (par_shape_par_ptr == null) cur_val = 0; else cur_val = vinfo(par_shape_par_ptr + 1); cur_val_level = int_val_level; break; case set_etex_shape_cmd: /*tex Fetch a penalties array element. */ scan_int(); if ((equiv(m) == null) || (cur_val < 0)) { cur_val = 0; } else { if (cur_val > penalty(equiv(m))) cur_val = penalty(equiv(m)); cur_val = penalty(equiv(m) + cur_val); } cur_val_level = int_val_level; break; case char_given_cmd: case math_given_cmd: case xmath_given_cmd: scanned_result(cur_chr, int_val_level); break; case last_item_cmd: /*tex Because the items in this case directly refer to |cur_chr|, it needs to be saved and restored. */ save_cur_chr = cur_chr; cur_chr = chr; /*tex Fetch an item in the current node, if appropriate. Here is where \.{\\lastpenalty}, \.{\\lastkern}, and \.{\\ } are implemented. The reference count for \.{\\lastskip} will be updated later. We also handle \.{\\inputlineno} and \.{\\badness} here, because they are legal in similar contexts. */ if (m >= input_line_no_code) { if (m >= eTeX_glue) { /*tex Process an expression and |return|. */ if (m < eTeX_mu) { if (m == mu_to_glue_code) { scan_mu_glue(); }; cur_val_level = glue_val_level; } else if (m < eTeX_expr) { if (m == glue_to_mu_code) { scan_normal_glue(); } cur_val_level = mu_val_level; } else { cur_val_level = m - eTeX_expr + int_val_level; scan_expr(); } /*tex This code for reducing |cur_val_level| and\slash or negating the result is similar to the one for all the other cases of |scan_something_internal|; we free a glue_spec when needed. */ while (cur_val_level > level) { downgrade_cur_val(true); } if (negative) { /*tex We get a new glue spec node with negated values and the old intermediate is deleted. */ negate_cur_val(true); } return succeeded; } else if (m >= eTeX_dim) { switch (m) { case font_char_wd_code: case font_char_ht_code: case font_char_dp_code: case font_char_ic_code: scan_font_ident(); q = cur_val; scan_char_num(); if (char_exists(q, cur_val)) { switch (m) { case font_char_wd_code: cur_val = char_width(q, cur_val); break; case font_char_ht_code: cur_val = char_height(q, cur_val); break; case font_char_dp_code: cur_val = char_depth(q, cur_val); break; case font_char_ic_code: cur_val = char_italic(q, cur_val); break; } } else { cur_val = 0; } break; case par_shape_length_code: case par_shape_indent_code: case par_shape_dimen_code: q = cur_chr - par_shape_length_code; scan_int(); if ((par_shape_par_ptr == null) || (cur_val <= 0)) { cur_val = 0; } else { if (q == 2) { q = cur_val % 2; cur_val = (cur_val + q) / 2; } if (cur_val > vinfo(par_shape_par_ptr + 1)) cur_val = vinfo(par_shape_par_ptr + 1); cur_val = varmem[par_shape_par_ptr + 2 * cur_val - q + 1].cint; } cur_val_level = dimen_val_level; break; case glue_stretch_code: case glue_shrink_code: scan_normal_glue(); q = cur_val; if (m == glue_stretch_code) cur_val = stretch(q); else cur_val = shrink(q); flush_node(q); break; } cur_val_level = dimen_val_level; } else { switch (m) { case input_line_no_code: cur_val = line; break; case badness_code: cur_val = last_badness; break; case luatex_version_code: cur_val = get_luatexversion(); break; case last_saved_box_resource_index_code: cur_val = last_saved_box_index; break; case last_saved_image_resource_index_code: cur_val = last_saved_image_index; break; case last_saved_image_resource_pages_code: cur_val = last_saved_image_pages; break; case last_x_pos_code: cur_val = last_position.h; break; case last_y_pos_code: cur_val = last_position.v; break; case random_seed_code: cur_val = random_seed; break; case eTeX_version_code: cur_val = eTeX_version; break; case eTeX_minor_version_code: cur_val = eTeX_minor_version; break; case current_group_level_code: cur_val = cur_level - level_one; break; case current_group_type_code: cur_val = cur_group; break; case current_if_level_code: q = cond_ptr; cur_val = 0; while (q != null) { incr(cur_val); q = vlink(q); } break; case current_if_type_code: if (cond_ptr == null) cur_val = 0; else if (cur_if < unless_code) cur_val = cur_if + 1; else cur_val = -(cur_if - unless_code + 1); break; case current_if_branch_code: if ((if_limit == or_code) || (if_limit == else_code)) cur_val = 1; else if (if_limit == fi_code) cur_val = -1; else cur_val = 0; break; case glue_stretch_order_code: case glue_shrink_order_code: case eTeX_glue_stretch_order_code: case eTeX_glue_shrink_order_code: scan_normal_glue(); q = cur_val; if (m == glue_stretch_order_code || m == eTeX_glue_stretch_order_code) { cur_val = stretch_order(q); } else { cur_val = shrink_order(q); } if (cur_val && (m == eTeX_glue_stretch_order_code || m == eTeX_glue_shrink_order_code)) { cur_val = (cur_val == 1) ? - 1 : cur_val - 1; } flush_node(q); break; } cur_val_level = int_val_level; } } else { if (cur_chr == glue_val_level) cur_val = zero_glue; else cur_val = 0; if (cur_chr == last_node_type_code) { cur_val_level = int_val_level; if ((cur_list.tail_field == cur_list.head_field) || (cur_list.mode_field == 0)) cur_val = -1; } else { /*tex assumes identical values */ cur_val_level = cur_chr; } if ((cur_list.tail_field != contrib_head) && (cur_list.mode_field != 0)) { switch (cur_chr) { case lastpenalty_code: if (type(cur_list.tail_field) == penalty_node) cur_val = penalty(cur_list.tail_field); break; case lastkern_code: if (type(cur_list.tail_field) == kern_node) cur_val = width(cur_list.tail_field); break; case lastskip_code: if (type(cur_list.tail_field) == glue_node) cur_val = cur_list.tail_field; if (subtype(cur_list.tail_field) == mu_glue) cur_val_level = mu_val_level; break; case last_node_type_code: cur_val = visible_last_node_type(cur_list.tail_field); break; } } else if ((cur_list.mode_field == vmode) && (cur_list.tail_field == cur_list.head_field)) { switch (cur_chr) { case lastpenalty_code: cur_val = last_penalty; break; case lastkern_code: cur_val = last_kern; break; case lastskip_code: if (last_glue != max_halfword) cur_val = last_glue; break; case last_node_type_code: cur_val = last_node_type; break; } } } cur_chr = save_cur_chr; break; default: succeeded = false; } if (succeeded) { while (cur_val_level > level) { /*tex Convert |cur_val| to a lower level. */ downgrade_cur_val(false); } /*tex Fix the reference count, if any, and negate |cur_val| if |negative|. If |cur_val| points to a glue specification at this point, the reference count for the glue does not yet include the reference by |cur_val|. If |negative| is |true|, |cur_val_level| is known to be |<=mu_val|. */ if (negative) { /*tex We create a new (negated) glue spec and keep the old one. */ negate_cur_val(false); } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) { cur_val = new_spec(cur_val); } } return succeeded; } /*tex First, here is a short routine that is called from lua code. All the real work is delegated to |short_scan_something_internal| that is shared between this routine and |scan_something_internal|. */ void scan_something_simple(halfword cmd, halfword subitem) { /*tex Negative is never true. */ if (!short_scan_something_internal(cmd, subitem, tok_val_level, false)) { /*tex Complain that |texlib| can not do this; give zero result. */ print_err("You can't use `"); print_cmd_chr((quarterword) cmd, subitem); tprint("' as tex library index"); help1("I'm forgetting what you said and using zero instead."); error(); scanned_result(0, int_val_level); } } /*tex OK, we're ready for |scan_something_internal| itself. A second parameter, |negative|, is set |true| if the value that is found should be negated. It is assumed that |cur_cmd| and |cur_chr| represent the first token of the internal quantity to be scanned; an error will be signalled if |cur_cmdmax_internal|. */ /*tex Fetch an internal parameter: */ void scan_something_internal(int level, boolean negative) { /*tex |chr_code| part of the operand token */ halfword m; /*tex accumulators */ int n, k; RESTART: m = cur_chr; if (!short_scan_something_internal(cur_cmd, cur_chr, level, negative)) { switch (cur_cmd) { case def_char_code_cmd: /*tex Fetch a character code from some table */ scan_char_num(); if (m == math_code_base) { cur_val1 = get_math_code_num(cur_val); scanned_result(cur_val1, int_val_level); } else if (m == lc_code_base) { cur_val1 = get_lc_code(cur_val); scanned_result(cur_val1, int_val_level); } else if (m == uc_code_base) { cur_val1 = get_uc_code(cur_val); scanned_result(cur_val1, int_val_level); } else if (m == sf_code_base) { cur_val1 = get_sf_code(cur_val); scanned_result(cur_val1, int_val_level); } else if (m == cat_code_base) { cur_val1 = get_cat_code(cat_code_table_par, cur_val); scanned_result(cur_val1, int_val_level); } else { confusion("def_char"); } break; case def_del_code_cmd: case extdef_del_code_cmd: /*tex Fetch a character code from some table. */ scan_char_num(); cur_val1 = get_del_code_num(cur_val); scanned_result(cur_val1, int_val_level); break; case extdef_math_code_cmd: /*tex Fetch an extended math code table value. */ scan_char_num(); cur_val1 = get_math_code_num(cur_val); scanned_result(cur_val1, int_val_level); break; case toks_register_cmd: case set_font_cmd: case def_font_cmd: case letterspace_font_cmd: case copy_font_cmd: /*tex Fetch a token list or font identifier, provided that |level=tok_val|. */ if (level != tok_val_level) { print_err("Missing number, treated as zero"); help3( "A number should have been here; I inserted `0'.", "(If you can't figure out why I needed to see a number,", "look up `weird error' in the index to The TeXbook.)" ); back_error(); scanned_result(0, dimen_val_level); } else if (cur_cmd == toks_register_cmd) { scan_register_num(); m = toks_base + cur_val; scanned_result(equiv(m), tok_val_level); } else { back_input(); scan_font_ident(); scanned_result(font_id_base + cur_val, ident_val_level); } break; case set_font_id_cmd: scan_int(); scanned_result(font_id_base + cur_val, ident_val_level); break; case def_family_cmd: /*tex Fetch a math font identifier. */ scan_char_num(); cur_val1 = fam_fnt(cur_val, m); scanned_result(font_id_base + cur_val1, ident_val_level); break; case set_math_param_cmd: /*tex Fetch a math parameter. */ cur_val1 = cur_chr; get_token(); if (cur_cmd != math_style_cmd) { print_err("Missing math style, treated as \\displaystyle"); help1("A style should have been here; I inserted `\\displaystyle'."); cur_val = display_style; back_error(); } else { cur_val = cur_chr; } if (cur_val1 < math_param_first_mu_glue) { if (cur_val1 == math_param_radical_degree_raise) { cur_val1 = get_math_param(cur_val1, cur_chr); scanned_result(cur_val1, int_val_level); } else { cur_val1 = get_math_param(cur_val1, cur_chr); scanned_result(cur_val1, dimen_val_level); } } else { cur_val1 = get_math_param(cur_val1, cur_chr); if (cur_val1 == thin_mu_skip_code) cur_val1 = thin_mu_skip_par; else if (cur_val1 == med_mu_skip_code) cur_val1 = med_mu_skip_par; else if (cur_val1 == thick_mu_skip_code) cur_val1 = thick_mu_skip_par; scanned_result(cur_val1, mu_val_level); } break; case assign_box_dir_cmd: scan_register_num(); m = cur_val; if (box(m) != null) cur_val = box_dir(box(m)); else cur_val = 0; cur_val_level = dir_val_level; break; case set_box_dimen_cmd: /*tex Fetch a box dimension. */ scan_register_num(); if (box(cur_val) == null) cur_val = 0; else cur_val = varmem[box(cur_val) + m].cint; cur_val_level = dimen_val_level; break; case assign_font_dimen_cmd: /*tex Fetch a font dimension. */ get_font_dimen(); break; case assign_font_int_cmd: /*tex Fetch a font integer. */ scan_font_ident(); if (m == 0) { scanned_result(hyphen_char(cur_val), int_val_level); } else if (m == 1) { scanned_result(skew_char(cur_val), int_val_level); } else if (m == no_lig_code) { scanned_result(test_no_ligatures(cur_val), int_val_level); } else { n = cur_val; scan_char_num(); k = cur_val; switch (m) { case lp_code_base: scanned_result(get_lp_code(n, k), int_val_level); break; case rp_code_base: scanned_result(get_rp_code(n, k), int_val_level); break; case ef_code_base: scanned_result(get_ef_code(n, k), int_val_level); break; case tag_code: scanned_result(get_tag_code(n, k), int_val_level); break; } } break; case register_cmd: /*tex Fetch a register */ scan_register_num(); switch (m) { case int_val_level: cur_val = count(cur_val); break; case attr_val_level: cur_val = attribute(cur_val); break; case dimen_val_level: cur_val = dimen(cur_val); break; case glue_val_level: cur_val = skip(cur_val); break; case mu_val_level: cur_val = mu_skip(cur_val); break; } cur_val_level = m; break; case ignore_spaces_cmd: /*tex Trap unexpandable primitives. */ if (cur_chr == 1) { /*tex Reset |cur_tok| for unexpandable primitives, goto restart. This block deals with unexpandable \.{\\primitive} appearing at a spot where an integer or an internal values should have been found. It fetches the next token then resets |cur_cmd|, |cur_cs|, and |cur_tok|, based on the primitive value of that token. No expansion takes place, because the next token may be all sorts of things. This could trigger further expansion creating new errors. */ get_token(); cur_cs = prim_lookup(cs_text(cur_cs)); if (cur_cs != undefined_primitive) { cur_cmd = get_prim_eq_type(cur_cs); cur_chr = get_prim_equiv(cur_cs); cur_tok = token_val(cur_cmd, cur_chr); } else { cur_cmd = relax_cmd; cur_chr = 0; cur_tok = cs_token_flag + frozen_relax; cur_cs = frozen_relax; } goto RESTART; } break; case hyph_data_cmd: switch (cur_chr) { case 0: case 1: goto DEFAULT; break; case 2: cur_val = get_pre_hyphen_char(language_par); cur_val_level = int_val_level; break; case 3: cur_val = get_post_hyphen_char(language_par); cur_val_level = int_val_level; break; case 4: cur_val = get_pre_exhyphen_char(language_par); cur_val_level = int_val_level; break; case 5: cur_val = get_post_exhyphen_char(language_par); cur_val_level = int_val_level; break; case 6: cur_val = get_hyphenation_min(language_par); cur_val_level = int_val_level; break; case 7: scan_int(); cur_val = get_hj_code(language_par,cur_val); cur_val_level = int_val_level; break; } break; default: DEFAULT: /*tex Complain that \.{\\the} can not do this; give zero result. */ print_err("You can't use `"); print_cmd_chr((quarterword) cur_cmd, cur_chr); tprint("' after \\the"); help1("I'm forgetting what you said and using zero instead."); error(); if (level != tok_val_level) scanned_result(0, dimen_val_level); else scanned_result(0, int_val_level); break; } while (cur_val_level > level) { /*tex Convert |cur_val| to a lower level. */ downgrade_cur_val(false); } /*tex Fix the reference count, if any, and negate |cur_val| if |negative|. If |cur_val| points to a glue specification at this point, the reference count for the glue does not yet include the reference by |cur_val|. If |negative| is |true|, |cur_val_level| is known to be |<=mu_val|. */ if (negative) { /*tex We create a new (negated) glue spec and keep the old one. */ negate_cur_val(false); } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) { cur_val = new_spec(cur_val); } } } /*tex It is nice to have routines that say what they do, so the original |scan_eight_bit_int| is superceded by |scan_register_num| and |scan_mark_num|. It may become split up even further in the future. Many of the |restricted classes| routines are the essentially the same except for the upper limit and the error message, so it makes sense to combine these all into one function. */ void scan_limited_int(int max, const char *name) { char hlp[80]; scan_int(); if ((cur_val < 0) || (cur_val > max)) { if (name == NULL) { snprintf(hlp, 80, "Since I expected to read a number between 0 and %d,", max); print_err("Bad number"); } else { char msg[80]; snprintf(hlp, 80, "A %s must be between 0 and %d.", name, max); snprintf(msg, 80, "Bad %s", name); print_err(msg); } help2(hlp, "I changed this one to zero."); int_error(cur_val); cur_val = 0; } } void scan_fifteen_bit_int(void) { scan_real_fifteen_bit_int(); cur_val = ((cur_val / 0x1000) * 0x1000000) + (((cur_val % 0x1000) / 0x100) * 0x10000) + (cur_val % 0x100); } void scan_fifty_one_bit_int(void) { int iiii; scan_int(); if ((cur_val < 0) || (cur_val > 0777777777)) { print_err("Bad delimiter code"); help2( "A numeric delimiter (first part) must be between 0 and 2^{27}-1.", "I changed this one to zero." ); int_error(cur_val); cur_val = 0; } iiii = cur_val; scan_int(); if ((cur_val < 0) || (cur_val > 0xFFFFFF)) { print_err("Bad delimiter code"); help2( "A numeric delimiter (second part) must be between 0 and 2^{24}-1.", "I changed this one to zero." ); int_error(cur_val); cur_val = 0; } cur_val1 = cur_val; cur_val = iiii; } /*tex An integer number can be preceded by any number of spaces and `\.+' or `\.-' signs. Then comes either a decimal constant (i.e., radix 10), an octal constant (i.e., radix 8, preceded by~'), a hexadecimal constant (radix 16, preceded by~"), an alphabetic constant (preceded by~`), or an internal variable. After scanning is complete, |cur_val| will contain the answer, which must be at most $2^{31}-1=2147483647$ in absolute value. The value of |radix| is set to 10, 8, or 16 in the cases of decimal, octal, or hexadecimal constants, otherwise |radix| is set to zero. An optional space follows a constant. */ /*tex |scan_int| sets this to 8, 10, 16, or zero */ int radix; /*tex The |scan_int| routine is used also to scan the integer part of a fraction; for example, the `\.3' in `\.{3.14159}' will be found by |scan_int|. The |scan_dimen| routine assumes that |cur_tok=point_token| after the integer part of such a fraction has been scanned by |scan_int|, and that the decimal point has been backed up to be scanned again. */ /*tex Sets |cur_val| to an integer: */ void scan_int(void) { /*tex should the answer be negated? */ boolean negative; /*tex |$2^{31}$ / radix|, the threshold of danger */ int m; /*tex the digit just scanned */ int d; /*tex have no digits appeared? */ boolean vacuous; /*tex has an error message been issued? */ boolean OK_so_far; radix = 0; OK_so_far = true; /*tex Get the next non-blank non-sign token; set |negative| appropriately. */ negative = false; do { /*tex Get the next non-blank non-call token. */ do { get_x_token(); } while (cur_cmd == spacer_cmd); if (cur_tok == other_token + '-') { negative = !negative; cur_tok = other_token + '+'; } } while (cur_tok == other_token + '+'); RESTART: if (cur_tok == alpha_token) { /*tex Scan an alphabetic character code into |cur_val|. A space is ignored after an alphabetic character constant, so that such constants behave like numeric ones. */ /*tex Suppress macro expansion: */ get_token(); if (cur_tok < cs_token_flag) { cur_val = cur_chr; if (cur_cmd <= right_brace_cmd) { if (cur_cmd == right_brace_cmd) incr(align_state); else decr(align_state); } } else { /*tex The value of a csname in this context is its name. */ str_number txt = cs_text(cur_tok - cs_token_flag); if (is_active_cs(txt)) cur_val = active_cs_value(txt); else if (single_letter(txt)) cur_val = pool_to_unichar(str_string(txt)); else cur_val = (biggest_char + 1); } if (cur_val > biggest_char) { print_err("Improper alphabetic constant"); help2( "A one-character control sequence belongs after a ` mark.", "So I'm essentially inserting \\0 here." ); cur_val = '0'; back_error(); } else { /*tex Scan an optional space. */ get_x_token(); if (cur_cmd != spacer_cmd) back_input(); } } else if (cur_tok == cs_token_flag + frozen_primitive) { /*tex Reset |cur_tok| for unexpandable primitives, goto restart This block deals with unexpandable \.{\\primitive} appearing at a spot where an integer or an internal values should have been found. It fetches the next token then resets |cur_cmd|, |cur_cs|, and |cur_tok|, based on the primitive value of that token. No expansion takes place, because the next token may be all sorts of things. This could trigger further expansion creating new errors. */ get_token(); cur_cs = prim_lookup(cs_text(cur_cs)); if (cur_cs != undefined_primitive) { cur_cmd = get_prim_eq_type(cur_cs); cur_chr = get_prim_equiv(cur_cs); cur_tok = token_val(cur_cmd, cur_chr); } else { cur_cmd = relax_cmd; cur_chr = 0; cur_tok = cs_token_flag + frozen_relax; cur_cs = frozen_relax; } goto RESTART; } else if (cur_cmd == math_style_cmd) { cur_val = cur_chr; } else if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { scan_something_internal(int_val_level, false); } else { /*tex Scan a numeric constant. */ radix = 10; m = 214748364; if (cur_tok == octal_token) { radix = 8; m = 02000000000; get_x_token(); } else if (cur_tok == hex_token) { radix = 16; m = 01000000000; get_x_token(); } vacuous = true; cur_val = 0; /*tex Accumulate the constant until |cur_tok| is not a suitable digit. */ while (1) { if ((cur_tok < zero_token + radix) && (cur_tok >= zero_token) && (cur_tok <= nine_token)) { d = cur_tok - zero_token; } else if (radix == 16) { if ((cur_tok <= A_token + 5) && (cur_tok >= A_token)) { d = cur_tok - A_token + 10; } else if ((cur_tok <= other_A_token + 5) && (cur_tok >= other_A_token)) { d = cur_tok - other_A_token + 10; } else { break; } } else { break; } vacuous = false; if ((cur_val >= m) && ((cur_val > m) || (d > 7) || (radix != 10))) { if (OK_so_far) { print_err("Number too big"); help2( "I can only go up to 2147483647='17777777777=\"7FFFFFFF,", "so I'm using that number instead of yours." ); error(); cur_val = infinity; OK_so_far = false; } } else { cur_val = cur_val * radix + d; } get_x_token(); } if (vacuous) { /*tex Express astonishment that no number was here */ print_err("Missing number, treated as zero"); help3( "A number should have been here; I inserted `0'.", "(If you can't figure out why I needed to see a number,", "look up `weird error' in the index to The TeXbook.)" ); back_error(); } else if (cur_cmd != spacer_cmd) { back_input(); } } if (negative) negate(cur_val); } /*tex The following code is executed when |scan_something_internal| was called asking for |mu_val|, when we really wanted a ``mudimen'' instead of ``muglue.'' */ static void coerce_glue(void) { int v; if (cur_val_level >= glue_val_level) { v = width(cur_val); flush_node(cur_val); cur_val = v; } } /*tex The |scan_dimen| routine is similar to |scan_int|, but it sets |cur_val| to a |scaled| value, i.e., an integral number of sp. One of its main tasks is therefore to interpret the abbreviations for various kinds of units and to convert measurements to scaled points. There are three parameters: |mu| is |true| if the finite units must be `\.{mu}', while |mu| is |false| if `\.{mu}' units are disallowed; |inf| is |true| if the infinite units `\.{fil}', `\.{fill}', `\.{filll}' are permitted; and |shortcut| is |true| if |cur_val| already contains an integer and only the units need to be considered. The order of infinity that was found in the case of infinite glue is returned in the global variable |cur_order|. */ /*tex The order of infinity found by |scan_dimen|: */ int cur_order; /*tex Constructions like `\.{-\'77 pt}' are legal dimensions, so |scan_dimen| may begin with |scan_int|. This explains why it is convenient to use |scan_int| also for the integer part of a decimal fraction. Several branches of |scan_dimen| work with |cur_val| as an integer and with an auxiliary fraction |f|, so that the actual quantity of interest is $|cur_val|+|f|/2^{16}$. At the end of the routine, this ``unpacked'' representation is put into the single word |cur_val|, which suddenly switches significance from |integer| to |scaled|. The necessary conversion factors can all be specified exactly as fractions whose numerator and denominator add to 32768 or less. According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$; this agrees well with the value $\rm1000.333\,mm$ cited by Bosshard \^{Bosshard, Hans Rudolf} in {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980). The Didot point has been newly standardized in 1978; it's now exactly $\rm 1\,nd=0.375\,mm$. Conversion uses the equation $0.375=21681/20320/72.27\cdot25.4$. The new Cicero follows the new Didot point; $\rm 1\,nc=12\,nd$. These would lead to the ratios $21681/20320$ and $65043/5080$, respectively. The closest approximations supported by the algorithm would be $11183/10481$ and $1370/107$. In order to maintain the relation $\rm 1\,nc=12\,nd$, we pick the ratio $685/642$ for $\rm nd$, however. */ static void scan_dimen_mu_error(void) { print_err("Illegal unit of measure (mu inserted)"); help4( "The unit of measurement in math glue must be mu.", "To recover gracefully from this error, it's best to", "delete the erroneous units; e.g., type `2' to delete", "two letters. (See Chapter 27 of The TeXbook.)" ); error(); } static void scan_dimen_unknown_unit_error(void) { print_err("Illegal unit of measure (pt inserted)"); help6( "Dimensions can be in units of em, ex, in, pt, pc,", "cm, mm, dd, cc, nd, nc, bp, or sp; but yours is a new one!", "I'll assume that you meant to say pt, for printer's points.", "To recover gracefully from this error, it's best to", "delete the erroneous units; e.g., type `2' to delete", "two letters. (See Chapter 27 of The TeXbook.)" ); error(); } static void scan_dimen_out_of_range_error(void) { print_err("Dimension too large"); help2( "I can't work with sizes bigger than about 19 feet.", "Continue and I'll use the largest value I can." ); error(); } #define set_conversion(A,B) do { num=(A); denom=(B); } while(0) /*tex This function sets |cur_val| to a dimension. It could be optimized a bit more (but not now, something for luatex > 1). */ void scan_dimen(boolean mu, boolean inf, boolean shortcut) { /*tex should the answer be negated? */ boolean negative = false; boolean is_true = false; /*tex numerator of a fraction whose denominator is $2^{16}$ */ int f = 0; /*tex conversion ratio for the scanned units */ int num = 0; int denom = 0; /*tex top of decimal digit stack */ halfword q; /*tex an internal dimension */ scaled v; /*tex temporary storage of |cur_val| */ int save_cur_val; arith_error = false; cur_order = normal; if (!shortcut) { /*tex Get the next non-blank non-sign. */ do { /*tex Get the next non-blank non-call token. */ do { get_x_token(); } while (cur_cmd == spacer_cmd); if (cur_tok == minus_token) { negative = !negative; cur_tok = plus_token; } } while (cur_tok == plus_token); if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { /*tex Fetch an internal dimension and |goto attach_sign|, or fetch an internal integer. */ if (mu) { scan_something_internal(mu_val_level, false); coerce_glue(); if (cur_val_level == mu_val_level) { goto ATTACH_SIGN; } else if (cur_val_level != int_val_level) { mu_error(); } } else { scan_something_internal(dimen_val_level, false); if (cur_val_level == dimen_val_level) { goto ATTACH_SIGN; } } } else { back_input(); if (cur_tok == continental_point_token) { cur_tok = point_token; } if (cur_tok != point_token) { scan_int(); } else { radix = 10; cur_val = 0; } if (cur_tok == continental_point_token) { cur_tok = point_token; } if ((radix == 10) && (cur_tok == point_token)) { /*tex Scan decimal fraction. When the following code is executed, we have |cur_tok=point_token|, but this token has been backed up using |back_input|; we must first discard it. It turns out that a decimal point all by itself is equivalent to `\.{0.0}'. Let's hope people don't use that fact. */ int k = 0; halfword p = null; int kk; /*tex The |point_token| is being re-scanned. */ get_token(); while (1) { get_x_token(); if ((cur_tok > nine_token) || (cur_tok < zero_token)) break; if (k < 17) { /*tex Digits for |k>=17| cannot affect the result. */ q = get_avail(); set_token_link(q, p); set_token_info(q, cur_tok - zero_token); p = q; incr(k); } } for (kk = k; kk >= 1; kk--) { dig[kk - 1] = token_info(p); q = p; p = token_link(p); free_avail(q); } f = round_decimals(k); if (cur_cmd != spacer_cmd) { back_input(); } } } } if (cur_val < 0) { /*tex In this case |f=0|. */ negative = !negative; negate(cur_val); } /*tex Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$, where there are |x| sp per unit; |goto attach_sign| if the units are internal. Now comes the harder part: At this point in the program, |cur_val| is a nonnegative integer and $f/2^{16}$ is a nonnegative fraction less than 1; we want to multiply the sum of these two quantities by the appropriate factor, based on the specified units, in order to produce a |scaled| result, and we want to do the calculation with fixed point arithmetic that does not overflow. */ if (inf) { if (scan_keyword("fi")) { cur_order = sfi; if (scan_keyword("l")) { cur_order = fil; if (scan_keyword("l")) { cur_order = fill; if (scan_keyword("l")) { cur_order = filll; } } } goto ATTACH_FRACTION; } } /*tex Scan for (u)units that are internal dimensions; |goto attach_sign| with |cur_val| set if found. */ save_cur_val = cur_val; /*tex Get the next non-blank non-call; a pitty if just backed up the input. */ do { get_x_token(); } while (cur_cmd == spacer_cmd); if ((cur_cmd < min_internal_cmd) || (cur_cmd > max_internal_cmd)) { back_input(); } else { /*tex |math_given_cmd|, |xmath_given_cmd| and |last_item_cmd} */ if (mu) { scan_something_internal(mu_val_level, false); coerce_glue(); if (cur_val_level != mu_val_level) { mu_error(); } } else { scan_something_internal(dimen_val_level, false); } v = cur_val; goto FOUND; } /*tex Bah |true| forces to split the unit scanner. */ if (mu) { /*tex Scan for (m)\.{mu} units and |goto attach_fraction|. */ if (! scan_keyword("mu")) { scan_dimen_mu_error(); } goto ATTACH_FRACTION; } else if (scan_keyword("em")) { v = quad(get_cur_font()); } else if (scan_keyword("ex")) { v = x_height(get_cur_font()); } else if (scan_keyword("px")) { v = px_dimen_par; } else { goto PICKUP_UNIT; } /*tex Scan an optional space (after em, ex or px) */ get_x_token(); if (cur_cmd != spacer_cmd) { back_input(); } FOUND: cur_val = nx_plus_y(save_cur_val, v, xn_over_d(v, f, 0200000)); goto ATTACH_SIGN; /*tex Scan for (a)all other units and adjust |cur_val| and |f| accordingly; |goto done| in the case of scaled points. */ PICKUP_UNIT: if (scan_keyword("pt")) { /*tex The easy case: */ goto SCALE_VALUE; } else if (scan_keyword("mm")) { set_conversion(7227, 2540); goto SCALE_VALUE; } else if (scan_keyword("cm")) { set_conversion(7227, 254); goto SCALE_VALUE; } else if (scan_keyword("sp")) { goto DONE; } else if (scan_keyword("bp")) { set_conversion(7227, 7200); goto SCALE_VALUE; } else if (scan_keyword("in")) { set_conversion(7227, 100); goto SCALE_VALUE; } else if (scan_keyword("dd")) { set_conversion(1238, 1157); goto SCALE_VALUE; } else if (scan_keyword("cc")) { set_conversion(14856, 1157); goto SCALE_VALUE; } else if (scan_keyword("pc")) { set_conversion(12, 1); goto SCALE_VALUE; } else if (scan_keyword("nd")) { set_conversion(685, 642); goto SCALE_VALUE; } else if (scan_keyword("nc")) { set_conversion(1370, 107); goto SCALE_VALUE; } else if (!is_true && scan_keyword("true")) { is_true = true; goto PICKUP_UNIT; } /*tex Complain about unknown unit and |goto done2|. */ scan_dimen_unknown_unit_error(); goto BAD_NEWS; SCALE_VALUE: /*tex Adjust |f| for the magnification ratio. */ if (is_true) { /*tex Maybe at some point we will drop mag completely, even in \DVI\ mode. */ if (output_mode_used <= OMODE_DVI) { prepare_mag(); if (mag_par != 1000) { cur_val = xn_over_d(cur_val, 1000, mag_par); f = (1000 * f + 0200000 * tex_remainder) / mag_par; cur_val = cur_val + (f / 0200000); f = f % 0200000; } } else { /*tex in \PDF\ mode we're always |true|. */ one_true_inch = one_inch; } } if (num) { cur_val = xn_over_d(cur_val, num, denom); f = (num * f + 0200000 * tex_remainder) / denom; cur_val = cur_val + (f / 0200000); f = f % 0200000; } BAD_NEWS: ATTACH_FRACTION: if (cur_val >= 040000) { arith_error = true; } else { cur_val = cur_val * unity + f; } DONE: /*tex Scan an optional space; this happens too often. */ get_x_token(); if (cur_cmd != spacer_cmd) { back_input(); } ATTACH_SIGN: if (arith_error || (abs(cur_val) >= 010000000000)) { /*tex Report that this dimension is out of range. */ scan_dimen_out_of_range_error(); cur_val = max_dimen; arith_error = false; } if (negative) { negate(cur_val); } } /*tex The final member of \TeX's value-scanning trio is |scan_glue|, which makes |cur_val| point to a glue specification. The reference count of that glue spec will take account of the fact that |cur_val| is pointing to~it. The |level| parameter should be either |glue_val| or |mu_val|. Since |scan_dimen| was so much more complex than |scan_int|, we might expect |scan_glue| to be even worse. But fortunately, it is very simple, since most of the work has already been done. */ void scan_glue(int level) { /*tex should the answer be negated? */ boolean negative = false; /*tex new glue specification */ halfword q = null; /*tex does |level=mu_val|? */ boolean mu = (level == mu_val_level); /*tex Get the next non-blank non-sign. */ do { /*tex Get the next non-blank non-call token. */ do { get_x_token(); } while (cur_cmd == spacer_cmd); if (cur_tok == minus_token) { negative = !negative; cur_tok = plus_token; } } while (cur_tok == plus_token); if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { scan_something_internal(level, negative); if (cur_val_level >= glue_val_level) { if (cur_val_level != level) mu_error(); return; } if (cur_val_level == int_val_level) scan_dimen(mu, false, true); else if (level == mu_val_level) mu_error(); } else { back_input(); scan_dimen(mu, false, false); if (negative) negate(cur_val); } /*tex Create a new glue specification whose width is |cur_val|; scan for its stretch and shrink components. */ q = new_spec(zero_glue); width(q) = cur_val; if (scan_keyword("plus")) { scan_dimen(mu, true, false); stretch(q) = cur_val; stretch_order(q) = (quarterword) cur_order; } if (scan_keyword("minus")) { scan_dimen(mu, true, false); shrink(q) = cur_val; shrink_order(q) = (quarterword) cur_order; } cur_val = q; } /*tex This procedure is supposed to scan something like `\.{\\skip\\count12}', i.e., whatever can follow `\.{\\the}', and it constructs a token list containing something like `\.{-3.0pt minus 0.5fill}'. */ halfword the_toks(void) { /*tex holds |selector| setting */ int old_setting; /*tex used for copying a token list */ halfword p, q, r; /*tex value of |cur_chr| */ int c; str_number s; halfword retval; /*tex Handle \.{\\unexpanded} or \.{\\detokenize} and |return|. */ if (odd(cur_chr)) { c = cur_chr; scan_general_text(); if (c == 1) { return cur_val; } else { old_setting = selector; selector = new_string; p = get_avail(); set_token_link(p, token_link(temp_token_head)); token_show(p); flush_list(p); selector = old_setting; s = make_string(); retval = str_toks(str_lstring(s)); flush_str(s); return retval; } } get_x_token(); scan_something_internal(tok_val_level, false); if (cur_val_level >= ident_val_level) { /*tex Copy the token list */ p = temp_token_head; set_token_link(p, null); if (cur_val_level == ident_val_level) { store_new_token(cs_token_flag + cur_val); } else if (cur_val != null) { /*tex Do not copy the reference count! */ r = token_link(cur_val); while (r != null) { fast_store_new_token(token_info(r)); r = token_link(r); } } return p; } else { old_setting = selector; selector = new_string; switch (cur_val_level) { case int_val_level: print_int(cur_val); break; case attr_val_level: print_int(cur_val); break; case dir_val_level: print_dir_par(cur_val); break; case dimen_val_level: print_scaled(cur_val); tprint("pt"); break; case glue_val_level: print_spec(cur_val, "pt"); flush_node(cur_val); break; case mu_val_level: print_spec(cur_val, "mu"); flush_node(cur_val); break; } selector = old_setting; s = make_string(); retval = str_toks(str_lstring(s)); flush_str(s); return retval; } } str_number the_scanned_result(void) { /*tex holds |selector| setting */ int old_setting; /*tex return value */ str_number r; old_setting = selector; selector = new_string; if (cur_val_level >= ident_val_level) { if (cur_val != null) { show_token_list(token_link(cur_val), null, -1); r = make_string(); } else { r = get_nullstr(); } } else { switch (cur_val_level) { case int_val_level: print_int(cur_val); break; case attr_val_level: print_int(cur_val); break; case dir_val_level: print_dir_par(cur_val); break; case dimen_val_level: print_scaled(cur_val); tprint("pt"); break; case glue_val_level: print_spec(cur_val, "pt"); flush_node(cur_val); break; case mu_val_level: print_spec(cur_val, "mu"); flush_node(cur_val); break; } r = make_string(); } selector = old_setting; return r; } /*tex The following routine is used to implement `\.{\\fontdimen} |n| |f|'. The boolean parameter |writing| is set |true| if the calling program intends to change the parameter value. */ static void font_param_error(int f) { print_err("Font "); print_esc(font_id_text(f)); tprint(" has only "); print_int(font_params(f)); tprint(" fontdimen parameters"); help2( "To increase the number of font parameters, you must", "use \\fontdimen immediately after the \\font is loaded." ); error(); } void set_font_dimen(void) { internal_font_number f; int n; scan_int(); n = cur_val; scan_font_ident(); f = cur_val; if (n <= 0) { font_param_error(f); } else if (n > font_params(f)) { if (font_used(f)) { font_param_error(f); } else { /*tex Increase the number of parameters in the font. */ do { set_font_param(f, (font_params(f) + 1), 0); } while (n != font_params(f)); } } scan_optional_equals(); scan_normal_dimen(); set_font_param(f, n, cur_val); } void get_font_dimen(void) { internal_font_number f; /*tex The parameter number: */ int n; scan_int(); n = cur_val; scan_font_ident(); f = cur_val; /*tex Initialize return value: */ cur_val = 0; if (n <= 0) { font_param_error(f); goto EXIT; } else if (n > font_params(f)) { if (font_used(f)) { font_param_error(f); goto EXIT; } else { /*tex Increase the number of parameters in the font. */ do { set_font_param(f, (font_params(f) + 1), 0); } while (n != font_params(f)); } } cur_val = font_param(f, n); EXIT: scanned_result(cur_val, dimen_val_level); } /*tex Here's a similar procedure that returns a pointer to a rule node. This routine is called just after \TeX\ has seen \.{\\hrule} or \.{\\vrule}; therefore |cur_cmd| will be either |hrule| or |vrule|. The idea is to store the default rule dimensions in the node, then to override them if `\.{height}' or `\.{width}' or `\.{depth}' specifications are found (in any order). */ halfword scan_rule_spec(void) { /*tex |width|, |depth|, and |height| all equal |null_flag| now */ halfword q; if (cur_cmd == no_vrule_cmd) { q = new_rule(empty_rule); cur_cmd = vrule_cmd; } else if (cur_cmd == no_hrule_cmd) { q = new_rule(empty_rule); cur_cmd = hrule_cmd; } else { q = new_rule(normal_rule); } if (cur_cmd == vrule_cmd) { width(q) = default_rule; rule_dir(q) = body_direction_par; } else { height(q) = default_rule; depth(q) = 0; rule_dir(q) = text_direction_par; } RESWITCH: if (scan_keyword("width")) { scan_normal_dimen(); width(q) = cur_val; goto RESWITCH; } if (scan_keyword("height")) { scan_normal_dimen(); height(q) = cur_val; goto RESWITCH; } if (scan_keyword("depth")) { scan_normal_dimen(); depth(q) = cur_val; goto RESWITCH; } return q; } /*tex Declare procedures that scan font-related stuff. */ void scan_font_ident(void) { internal_font_number f; halfword m; /*tex Get the next non-blank non-call. */ do { get_x_token(); } while (cur_cmd == spacer_cmd); if ((cur_cmd == def_font_cmd) || (cur_cmd == letterspace_font_cmd) || (cur_cmd == copy_font_cmd)) { f = get_cur_font(); } else if (cur_cmd == set_font_cmd) { f = cur_chr; set_font_touched(f, 1); } else if (cur_cmd == def_family_cmd) { m = cur_chr; scan_math_family_int(); f = fam_fnt(cur_val, m); set_font_touched(f, 1); } else { print_err("Missing font identifier"); help2( "I was looking for a control sequence whose", "current meaning has been defined by \\font." ); back_error(); f = null_font; } cur_val = f; } /*tex The |scan_general_text| procedure is much like |scan_toks(false,false)|, but will be invoked via |expand|, i.e., recursively. The token list (balanced text) created by |scan_general_text| begins at |link(temp_token_head)| and ends at |cur_val|. (If |cur_val=temp_token_head|, the list is empty.) */ void scan_general_text(void) { /*tex Here we save |scanner_status|: */ int s; /*tex Here we save |warning_index|: */ halfword w; /*tex Here we save |def_ref|: */ halfword d; /*tex The tail of the token list being built: */ halfword p; /*tex The new node being added to the token list via |store_new_token|: */ halfword q; /*tex The number of unmatched left braces: */ halfword unbalance; s = scanner_status; w = warning_index; d = def_ref; scanner_status = absorbing; warning_index = cur_cs; p = get_avail(); def_ref = p; set_token_ref_count(def_ref, 0); p = def_ref; /*tex Remove the compulsory left brace. */ scan_left_brace(); unbalance = 1; while (1) { get_token(); if (cur_tok < right_brace_limit) { if (cur_cmd < right_brace_cmd) { incr(unbalance); } else { decr(unbalance); if (unbalance == 0) break; } } store_new_token(cur_tok); } q = token_link(def_ref); /*tex Discard reference count. */ free_avail(def_ref); if (q == null) cur_val = temp_token_head; else cur_val = p; set_token_link(temp_token_head, q); scanner_status = s; warning_index = w; def_ref = d; } /*tex The |get_x_or_protected| procedure is like |get_x_token| except that protected macros are not expanded. It sets |cur_cmd|, |cur_chr|, |cur_tok|,and expands non-protected macros. */ void get_x_or_protected(void) { while (1) { get_token(); if (cur_cmd <= max_command_cmd) return; if ((cur_cmd >= call_cmd) && (cur_cmd < end_template_cmd)) { if (token_info(token_link(cur_chr)) == protected_token) return; } expand(); } } /*tex |scan_toks|. This function returns a pointer to the tail of a new token list, and it also makes |def_ref| point to the reference count at the head of that list. There are two boolean parameters, |macro_def| and |xpand|. If |macro_def| is true, the goal is to create the token list for a macro definition; otherwise the goal is to create the token list for some other \TeX\ primitive: \.{\\mark}, \.{\\output}, \.{\\everypar}, \.{\\lowercase}, \.{\\uppercase}, \.{\\message}, \.{\\errmessage}, \.{\\write}, or \.{\\special}. In the latter cases a left brace must be scanned next; this left brace will not be part of the token list, nor will the matching right brace that comes at the end. If |xpand| is false, the token list will simply be copied from the input using |get_token|. Otherwise all expandable tokens will be expanded until unexpandable tokens are left, except that the results of expanding `\.{\\the}' are not expanded further. If both |macro_def| and |xpand| are true, the expansion applies only to the macro body (i.e., to the material following the first |left_brace| character). The value of |cur_cs| when |scan_toks| begins should be the |eqtb| address of the control sequence to display in ``runaway'' error messages. */ halfword scan_toks(boolean macro_def, boolean xpand) { /*tex token representing the highest parameter number */ halfword t; /*tex saved token */ halfword s; /*tex tail of the token list being built */ halfword p; /*tex new node being added to the token list via |store_new_token| */ halfword q; /*tex number of unmatched left braces */ halfword unbalance; /*tex possible `\.{\#\{}' token */ halfword hash_brace; if (macro_def) scanner_status = defining; else scanner_status = absorbing; warning_index = cur_cs; p = get_avail(); def_ref = p; set_token_ref_count(def_ref, 0); p = def_ref; hash_brace = 0; t = zero_token; if (macro_def) { /*tex Scan and build the parameter part of the macro definition. */ while (1) { /*tex Set |cur_cmd|, |cur_chr|, |cur_tok|: */ get_token(); if (cur_tok < right_brace_limit) break; if (cur_cmd == mac_param_cmd) { /*tex If the next character is a parameter number, make |cur_tok| a |match| token; but if it is a left brace, store `|left_brace|, |end_match|', set |hash_brace|, and |goto done|. */ s = match_token + cur_chr; get_token(); if (cur_cmd == left_brace_cmd) { hash_brace = cur_tok; store_new_token(cur_tok); store_new_token(end_match_token); goto DONE; } if (t == nine_token) { print_err("You already have nine parameters"); help1("I'm going to ignore the # sign you just used."); error(); } else { incr(t); if (cur_tok != t) { print_err("Parameters must be numbered consecutively"); help2( "I've inserted the digit you should have used after the #.", "Type `1' to delete what you did use." ); back_error(); } cur_tok = s; } } store_new_token(cur_tok); } store_new_token(end_match_token); if (cur_cmd == right_brace_cmd) { /*tex Express shock at the missing left brace; |goto found|. */ print_err("Missing { inserted"); incr(align_state); help2( "Where was the left brace? You said something like `\\def\\a}',", "which I'm going to interpret as `\\def\\a{}'." ); error(); goto FOUND; } } else { /*tex Remove the compulsory left brace. */ scan_left_brace(); } DONE: /*tex Scan and build the body of the token list; |goto found| when finished. */ unbalance = 1; while (1) { if (xpand) { /*tex Expand the next part of the input. Here we insert an entire token list created by |the_toks| without expanding it further. */ while (1) { get_next(); if (cur_cmd >= call_cmd) { if (token_info(token_link(cur_chr)) == protected_token) { cur_cmd = relax_cmd; cur_chr = no_expand_flag; } } if (cur_cmd <= max_command_cmd) break; if (cur_cmd != the_cmd) { expand(); } else { q = the_toks(); if (token_link(temp_token_head) != null) { set_token_link(p, token_link(temp_token_head)); p = q; } } } x_token(); } else { get_token(); } if (cur_tok < right_brace_limit) { if (cur_cmd < right_brace_cmd) { incr(unbalance); } else { decr(unbalance); if (unbalance == 0) goto FOUND; } } else if (cur_cmd == mac_param_cmd) { if (macro_def) { /*tex Look for parameter number or \.{\#\#}. */ s = cur_tok; if (xpand) get_x_token(); else get_token(); if (cur_cmd != mac_param_cmd) { if ((cur_tok <= zero_token) || (cur_tok > t)) { print_err("Illegal parameter number in definition of "); sprint_cs(warning_index); help3( "You meant to type ## instead of #, right?", "Or maybe a } was forgotten somewhere earlier, and things", "are all screwed up? I'm going to assume that you meant ##." ); back_error(); cur_tok = s; } else { cur_tok = out_param_token - '0' + cur_chr; } } } } store_new_token(cur_tok); } FOUND: scanner_status = normal; if (hash_brace != 0) store_new_token(hash_brace); return p; } /*tex Here we declare two trivial procedures in order to avoid mutually recursive procedures with parameters. */ void scan_normal_glue(void) { scan_glue(glue_val_level); } void scan_mu_glue(void) { scan_glue(mu_val_level); } /*tex The |scan_expr| procedure scans and evaluates an expression. Evaluating an expression is a recursive process: When the left parenthesis of a subexpression is scanned we descend to the next level of recursion; the previous level is resumed with the matching right parenthesis. */ typedef enum { /*tex \.( seen, or \.( $\langle\it expr\rangle$ \.) seen */ expr_none = 0, /*tex \.( $\langle\it expr\rangle$ \.+ seen */ expr_add = 1, /*tex \.( $\langle\it expr\rangle$ \.- seen */ expr_sub = 2, /*tex $\langle\it term\rangle$ \.* seen */ expr_mult = 3, /*tex $\langle\it term\rangle$ \./ seen */ expr_div = 4, /*tex $\langle\it term\rangle$ \.* $\langle\it factor\rangle$ \./ seen */ expr_scale = 5, } expression_states; /*tex We want to make sure that each term and (intermediate) result is in the proper range. Integer values must not exceed |infinity| ($2^{31}-1$) in absolute value, dimensions must not exceed |max_dimen| ($2^{30}-1$). We avoid the absolute value of an integer, because this might fail for the value $-2^{31}$ using 32-bit arithmetic. */ /* Clear a number or dimension and set |arith_error|: */ #define num_error(A) do { \ arith_error=true; \ A=0; \ } while (0) /*tex Clear a glue spec and set |arith_error|: */ #define glue_error(A) do { \ arith_error=true; \ reset_glue_to_zero(A); \ } while (0) #define normalize_glue(A) do { \ if (stretch(A)==0) stretch_order(A)=normal; \ if (shrink(A)==0) shrink_order(A)=normal; \ } while (0) /*tex Parenthesized subexpressions can be inside expressions, and this nesting has a stack. Seven local variables represent the top of the expression stack: |p| points to pushed-down entries, if any; |l| specifies the type of expression currently beeing evaluated; |e| is the expression so far and |r| is the state of its evaluation; |t| is the term so far and |s| is the state of its evaluation; finally |n| is the numerator for a combined multiplication and division, if any. */ #define expr_add_sub(A,B,C) add_or_sub((A),(B),(C),(r==expr_sub)) #define expr_a(A,B) expr_add_sub((A),(B),max_dimen) /*tex The function |add_or_sub(x,y,max_answer,negative)| computes the sum (for |negative=false|) or difference (for |negative=true|) of |x| and |y|, provided the absolute value of the result does not exceed |max_answer|. */ inline static int add_or_sub(int x, int y, int max_answer, boolean negative) { int a; if (negative) negate(y); if (x >= 0) { if (y <= max_answer - x) a = x + y; else num_error(a); } else if (y >= -max_answer - x) { a = x + y; } else { num_error(a); } return a; } #define expr_m(A) A = nx_plus_y((A),f,0) #define expr_d(A) A=quotient((A),f) /*tex The function |quotient(n,d)| computes the rounded quotient $q=\lfloor n/d+{1\over2}\rfloor$, when $n$ and $d$ are positive. */ inline static int quotient(int n, int d) { /*tex Should the answer be negated? */ boolean negative; /*tex The answer: */ int a; if (d == 0) { num_error(a); } else { if (d > 0) { negative = false; } else { negate(d); negative = true; } if (n < 0) { negate(n); negative = !negative; } a = n / d; n = n - a * d; /*tex Avoid certain compiler optimizations! */ d = n - d; if (d + n >= 0) incr(a); if (negative) negate(a); } return a; } #define expr_s(A) A=fract((A),n,f,max_dimen) /*tex Finally, the function |fract(x,n,d,max_answer)| computes the integer $q=\lfloor xn/d+{1\over2}\rfloor$, when $x$, $n$, and $d$ are positive and the result does not exceed |max_answer|. We can't use floating point arithmetic since the routine must produce identical results in all cases; and it would be too dangerous to multiply by~|n| and then divide by~|d|, in separate operations, since overflow might well occur. Hence this subroutine simulates double precision arithmetic, somewhat analogous to Metafont's |make_fraction| and |take_fraction| routines. */ int fract(int x, int n, int d, int max_answer) { /*tex should the answer be negated? */ boolean negative; /*tex the answer */ int a; /*tex a proper fraction */ int f; /*tex smallest integer such that |2*h>=d| */ int h; /*tex intermediate remainder */ int r; /*tex temp variable */ int t; if (d == 0) goto TOO_BIG; a = 0; if (d > 0) { negative = false; } else { negate(d); negative = true; } if (x < 0) { negate(x); negative = !negative; } else if (x == 0) { goto DONE; } if (n < 0) { negate(n); negative = !negative; } t = n / d; if (t > max_answer / x) goto TOO_BIG; a = t * x; n = n - t * d; if (n == 0) goto FOUND; t = x / d; if (t > (max_answer - a) / n) goto TOO_BIG; a = a + t * n; x = x - t * d; if (x == 0) goto FOUND; if (x < n) { t = x; x = n; n = t; } /*tex Now |0= 0) { r = r - d; incr(f); } } n = n / 2; if (n == 0) break; if (x < h) { x = x + x; } else { t = x - d; x = t + x; f = f + n; if (x < n) { if (x == 0) break; t = x; x = n; n = t; } } } if (f > (max_answer - a)) goto TOO_BIG; a = a + f; FOUND: if (negative) negate(a); goto DONE; TOO_BIG: num_error(a); DONE: return a; } /*tex Scans and evaluates an expression: */ static void scan_expr(void) { /*tex saved values of |arith_error| */ boolean a, b; /*tex type of expression */ int l; /*tex state of expression so far */ int r; /*tex state of term so far */ int s; /*tex next operation or type of next factor */ int o; /*tex expression so far */ int e; /*tex term so far */ int t; /*tex current factor */ int f; /*tex numerator of combined multiplication and division */ int n; /*tex top of expression stack */ halfword p; /*tex for stack manipulations */ halfword q; l = cur_val_level; a = arith_error; b = false; p = null; /*tex Scan and evaluate an expression |e| of type |l|. */ RESTART: r = expr_none; e = 0; s = expr_none; t = 0; n = 0; CONTINUE: if (s == expr_none) o = l; else o = int_val_level; /*tex Scan a factor |f| of type |o| or start a subexpression. Get the next non-blank non-call token. */ do { get_x_token(); } while (cur_cmd == spacer_cmd); if (cur_tok == other_token + '(') { /*tex Push the expression stack and |goto restart|. */ q = new_node(expr_node, 0); vlink(q) = p; expr_type(q) = (quarterword) l; expr_state(q) = (quarterword) (4 * s + r); expr_e_field(q) = e; expr_t_field(q) = t; expr_n_field(q) = n; p = q; l = o; goto RESTART; } back_input(); if ((o == int_val_level) || (o == attr_val_level)) scan_int(); else if (o == dimen_val_level) scan_normal_dimen(); else if (o == glue_val_level) scan_normal_glue(); else scan_mu_glue(); f = cur_val; FOUND: /*tex Scan the next operator and set |o| and Get the next non-blank non-call token. */ do { get_x_token(); } while (cur_cmd == spacer_cmd); if (cur_tok == other_token + '+') { o = expr_add; } else if (cur_tok == other_token + '-') { o = expr_sub; } else if (cur_tok == other_token + '*') { o = expr_mult; } else if (cur_tok == other_token + '/') { o = expr_div; } else { o = expr_none; if (p == null) { if (cur_cmd != relax_cmd) back_input(); } else if (cur_tok != other_token + ')') { print_err("Missing ) inserted for expression"); help1("I was expecting to see `+', `-', `*', `/', or `)'. Didn't."); back_error(); } } arith_error = b; /*tex Make sure that |f| is in the proper range. */ if (((l == int_val_level) || (l == attr_val_level)) || (s > expr_sub)) { if ((f > infinity) || (f < -infinity)) num_error(f); } else if (l == dimen_val_level) { if (abs(f) > max_dimen) num_error(f); } else { if ((abs(width(f)) > max_dimen) || (abs(stretch(f)) > max_dimen) || (abs(shrink(f)) > max_dimen)) glue_error(f); } /*tex Cases for evaluation of the current term. */ switch (s) { case expr_none: /*tex Applying the factor |f| to the partial term |t| (with the operator |s|) is delayed until the next operator |o| has been scanned. Here we handle the first factor of a partial term. A glue spec has to be copied unless the next operator is a right parenthesis; this allows us later on to simply modify the glue components. */ t = f; if ((l >= glue_val_level) && (o != expr_none)) { /*tex Do we really need to copy here? */ normalize_glue(t); } else { t = f; } break; case expr_mult: /*tex If a multiplication is followed by a division, the two operations are combined into a `scaling' operation. Otherwise the term |t| is multiplied by the factor |f|. */ if (o == expr_div) { n = f; o = expr_scale; } else if ((l == int_val_level) || (l == attr_val_level)) { t = mult_integers(t, f); } else if (l == dimen_val_level) { expr_m(t); } else { expr_m(width(t)); expr_m(stretch(t)); expr_m(shrink(t)); } break; case expr_div: /*tex Here we divide the term |t| by the factor |f|. */ if (l < glue_val_level) { expr_d(t); } else { expr_d(width(t)); expr_d(stretch(t)); expr_d(shrink(t)); } break; case expr_scale: /*tex Here the term |t| is multiplied by the quotient $n/f$. */ if ((l == int_val_level) || (l == attr_val_level)) { t = fract(t, n, f, infinity); } else if (l == dimen_val_level) { expr_s(t); } else { expr_s(width(t)); expr_s(stretch(t)); expr_s(shrink(t)); } break; } if (o > expr_sub) { s = o; } else { /*tex Evaluate the current expression. When a term |t| has been completed it is copied to, added to, or subtracted from the expression |e|. */ s = expr_none; if (r == expr_none) { e = t; } else if ((l == int_val_level) || (l == attr_val_level)) { e = expr_add_sub(e, t, infinity); } else if (l == dimen_val_level) { e = expr_a(e, t); } else { /*tex Compute the sum or difference of two glue specs. We know that |stretch_order(e)>normal| implies |stretch(e)<>0| and |shrink_order(e)>normal| implies |shrink(e)<>0|. */ width(e) = expr_a(width(e), width(t)); if (stretch_order(e) == stretch_order(t)) { stretch(e) = expr_a(stretch(e), stretch(t)); } else if ((stretch_order(e) < stretch_order(t)) && (stretch(t) != 0)) { stretch(e) = stretch(t); stretch_order(e) = stretch_order(t); } if (shrink_order(e) == shrink_order(t)) { shrink(e) = expr_a(shrink(e), shrink(t)); } else if ((shrink_order(e) < shrink_order(t)) && (shrink(t) != 0)) { shrink(e) = shrink(t); shrink_order(e) = shrink_order(t); } flush_node(t); normalize_glue(e); } r = o; } b = arith_error; if (o != expr_none) goto CONTINUE; if (p != null) { /*tex Pop the expression stack and |goto found|. */ f = e; q = p; e = expr_e_field(q); t = expr_t_field(q); n = expr_n_field(q); s = expr_state(q) / 4; r = expr_state(q) % 4; l = expr_type(q); p = vlink(q); flush_node(q); goto FOUND; } if (b) { print_err("Arithmetic overflow"); help2( "I can't evaluate this expression,", "since the result is out of range." ); error(); if (l >= glue_val_level) { reset_glue_to_zero(e); } else { e = 0; } } arith_error = a; cur_val = e; cur_val_level = l; }