summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/luatexdir/tex/extensions.w
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex/extensions.w')
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/extensions.w1217
1 files changed, 1217 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/extensions.w b/Build/source/texk/web2c/luatexdir/tex/extensions.w
new file mode 100644
index 00000000000..b8b89512ba2
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/extensions.w
@@ -0,0 +1,1217 @@
+% extensions.w
+
+% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+% 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 <http://www.gnu.org/licenses/>.
+
+\def\eTeX{e-\TeX}
+\def\pdfTeX{pdf\TeX}
+
+@ @c
+static const char _svn_version[] =
+ "$Id: extensions.w 3587 2010-04-03 14:32:25Z taco $"
+ "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/extensions.w $";
+
+#include "ptexlib.h"
+
+@ @c
+#define mode cur_list.mode_field
+#define tail cur_list.tail_field
+#define head cur_list.head_field
+#define prev_graf cur_list.pg_field
+#define dir_save cur_list.dirs_field
+
+#define tracing_nesting int_par(tracing_nesting_code)
+#define box(A) eqtb[box_base+(A)].hh.rh
+#define global_defs int_par(global_defs_code)
+#define cat_code_table int_par(cat_code_table_code)
+#define par_direction int_par(par_direction_code)
+#define toks(A) equiv(toks_base+(A))
+
+#define local_inter_line_penalty int_par(local_inter_line_penalty_code)
+#define local_broken_penalty int_par(local_broken_penalty_code)
+#define local_left_box equiv(local_left_box_base)
+#define local_right_box equiv(local_right_box_base)
+
+
+@ The program above includes a bunch of ``hooks'' that allow further
+capabilities to be added without upsetting \TeX's basic structure.
+Most of these hooks are concerned with ``whatsit'' nodes, which are
+intended to be used for special purposes; whenever a new extension to
+\TeX\ involves a new kind of whatsit node, a corresponding change needs
+to be made to the routines below that deal with such nodes,
+but it will usually be unnecessary to make many changes to the
+other parts of this program.
+
+In order to demonstrate how extensions can be made, we shall treat
+`\.{\\write}', `\.{\\openout}', `\.{\\closeout}', `\.{\\immediate}',
+and `\.{\\special}' as if they were extensions.
+These commands are actually primitives of \TeX, and they should
+appear in all implementations of the system; but let's try to imagine
+that they aren't. Then the program below illustrates how a person
+could add them.
+
+Sometimes, of course, an extension will require changes to \TeX\ itself;
+no system of hooks could be complete enough for all conceivable extensions.
+The features associated with `\.{\\write}' are almost all confined to the
+following paragraphs, but there are small parts of the |print_ln| and
+|print_char| procedures that were introduced specifically to \.{\\write}
+characters. Furthermore one of the token lists recognized by the scanner
+is a |write_text|; and there are a few other miscellaneous places where we
+have already provided for some aspect of \.{\\write}. The goal of a \TeX\
+extender should be to minimize alterations to the standard parts of the
+program, and to avoid them completely if possible. He or she should also
+be quite sure that there's no easy way to accomplish the desired goals
+with the standard features that \TeX\ already has. ``Think thrice before
+extending,'' because that may save a lot of work, and it will also keep
+incompatible extensions of \TeX\ from proliferating.
+@^system dependencies@>
+@^extensions to \TeX@>
+
+First let's consider the format of whatsit nodes that are used to represent
+the data associated with \.{\\write} and its relatives. Recall that a whatsit
+has |type=whatsit_node|, and the |subtype| is supposed to distinguish
+different kinds of whatsits. Each node occupies two or more words; the
+exact number is immaterial, as long as it is readily determined from the
+|subtype| or other data.
+
+We shall introduce five |subtype| values here, corresponding to the
+control sequences \.{\\openout}, \.{\\write}, \.{\\closeout}, and \.{\\special}.
+The second word of I/O whatsits has a |write_stream| field
+that identifies the write-stream number (0 to 15, or 16 for out-of-range and
+positive, or 17 for out-of-range and negative).
+In the case of \.{\\write} and \.{\\special}, there is also a field that
+points to the reference count of a token list that should be sent. In the
+case of \.{\\openout}, we need three words and three auxiliary subfields
+to hold the string numbers for name, area, and extension.
+
+@ Extensions might introduce new command codes; but it's best to use
+|extension| with a modifier, whenever possible, so that |main_control|
+stays the same.
+
+@ The sixteen possible \.{\\write} streams are represented by the |write_file|
+array. The |j|th file is open if and only if |write_open[j]=true|. The last
+two streams are special; |write_open[16]| represents a stream number
+greater than 15, while |write_open[17]| represents a negative stream number,
+and both of these variables are always |false|.
+
+@c
+alpha_file write_file[16];
+halfword write_file_mode[16];
+halfword write_file_translation[16];
+boolean write_open[18];
+scaled neg_wd;
+scaled pos_wd;
+scaled neg_ht;
+
+
+@ The variable |write_loc| just introduced is used to provide an
+appropriate error message in case of ``runaway'' write texts.
+
+@c
+halfword write_loc; /* |eqtb| address of \.{\\write} */
+
+
+@ When an |extension| command occurs in |main_control|, in any mode,
+the |do_extension| routine is called.
+
+@c
+void do_extension(PDF pdf)
+{
+ int i, k; /* all-purpose integers */
+ halfword p; /* all-purpose pointer */
+ switch (cur_chr) {
+ case open_node:
+ /* Implement \.{\\openout} */
+ new_write_whatsit(open_node_size);
+ scan_optional_equals();
+ scan_file_name();
+ open_name(tail) = cur_name;
+ open_area(tail) = cur_area;
+ open_ext(tail) = cur_ext;
+ break;
+ case write_node:
+ /* Implement \.{\\write} */
+ /* When `\.{\\write 12\{...\}}' appears, we scan the token list `\.{\{...\}}'
+ without expanding its macros; the macros will be expanded later when this
+ token list is rescanned. */
+ k = cur_cs;
+ new_write_whatsit(write_node_size);
+ cur_cs = k;
+ p = scan_toks(false, false);
+ write_tokens(tail) = def_ref;
+ break;
+ case close_node:
+ /* Implement \.{\\closeout} */
+ new_write_whatsit(write_node_size);
+ write_tokens(tail) = null;
+ break;
+ case special_node:
+ /* Implement \.{\\special} */
+ /* When `\.{\\special\{...\}}' appears, we expand the macros in the token
+ list as in \.{\\xdef} and \.{\\mark}. */
+ new_whatsit(special_node);
+ write_stream(tail) = null;
+ p = scan_toks(false, true);
+ write_tokens(tail) = def_ref;
+ break;
+ case immediate_code:
+ /* Implement \.{\\immediate} */
+ /* To write a token list, we must run it through \TeX's scanner, expanding
+ macros and \.{\\the} and \.{\\number}, etc. This might cause runaways,
+ if a delimited macro parameter isn't matched, and runaways would be
+ extremely confusing since we are calling on \TeX's scanner in the middle
+ of a \.{\\shipout} command. Therefore we will put a dummy control sequence as
+ a ``stopper,'' right after the token list. This control sequence is
+ artificially defined to be \.{\\outer}.
+
+ The presence of `\.{\\immediate}' causes the |do_extension| procedure
+ to descend to one level of recursion. Nothing happens unless \.{\\immediate}
+ is followed by `\.{\\openout}', `\.{\\write}', or `\.{\\closeout}'.
+ */
+ get_x_token();
+ if (cur_cmd == extension_cmd) {
+ if (cur_chr <= close_node) {
+ p = tail;
+ /* |do_extension()| and |out_what()| here can only be open, write, or close */
+ do_extension(pdf); /* append a whatsit node */
+ out_what(pdf, tail); /* do the action immediately */
+ flush_node_list(tail);
+ tail = p;
+ vlink(p) = null;
+ } else {
+ switch (cur_chr) {
+ case pdf_obj_code:
+ check_o_mode(pdf, "\\immediate\\pdfobj", 1 << OMODE_PDF,
+ true);
+ do_extension(pdf); /* scan object and set |pdf_last_obj| */
+ if (obj_data_ptr(pdf, pdf_last_obj) == 0) /* this object has not been initialized yet */
+ pdf_error("ext1",
+ "`\\pdfobj reserveobjnum' cannot be used with \\immediate");
+ pdf_write_obj(pdf, pdf_last_obj);
+ break;
+ case pdf_xform_code:
+ check_o_mode(pdf, "\\immediate\\pdfxform", 1 << OMODE_PDF,
+ true);
+ do_extension(pdf); /* scan form and set |pdf_last_xform| */
+ pdf_cur_form = pdf_last_xform;
+ ship_out(pdf, obj_xform_box(pdf, pdf_last_xform), false);
+ break;
+ case pdf_ximage_code:
+ check_o_mode(pdf, "\\immediate\\pdfximage", 1 << OMODE_PDF,
+ true);
+ do_extension(pdf); /* scan image and set |pdf_last_ximage| */
+ pdf_write_image(pdf, pdf_last_ximage);
+ break;
+ default:
+ back_input();
+ break;
+ }
+ }
+ } else {
+ back_input();
+ }
+ break;
+ case pdf_annot_node:
+ /* Implement \.{\\pdfannot} */
+ check_o_mode(pdf, "\\pdfannot", 1 << OMODE_PDF, false);
+ scan_annot(pdf);
+ break;
+ case pdf_catalog_code:
+ /* Implement \.{\\pdfcatalog} */
+ check_o_mode(pdf, "\\pdfcatalog", 1 << OMODE_PDF, true); /* writes an object */
+ scan_pdfcatalog(pdf);
+ break;
+ case pdf_dest_node:
+ /* Implement \.{\\pdfdest} */
+ check_o_mode(pdf, "\\pdfdest", 1 << OMODE_PDF, false);
+ scan_pdfdest(pdf);
+ break;
+ case pdf_end_link_node:
+ /* Implement \.{\\pdfendlink} */
+ check_o_mode(pdf, "\\pdfendlink", 1 << OMODE_PDF, false);
+ if (abs(mode) == vmode)
+ pdf_error("ext1", "\\pdfendlink cannot be used in vertical mode");
+ new_whatsit(pdf_end_link_node);
+ break;
+ case pdf_end_thread_node:
+ /* Implement \.{\\pdfendthread} */
+ check_o_mode(pdf, "\\pdfendthread", 1 << OMODE_PDF, false);
+ new_whatsit(pdf_end_thread_node);
+ break;
+ case pdf_font_attr_code:
+ /* Implement \.{\\pdffontattr} */
+ /* A change from Thanh's original code: the font attributes are simply
+ initialized to zero now, this is easier to deal with from C than an
+ empty \TeX{} string, and surely nobody will want to set
+ \.{\\pdffontattr} to a string containing a single zero, as that
+ would be nonsensical in the PDF output. */
+ check_o_mode(pdf, "\\pdffontattr", 1 << OMODE_PDF, false);
+ scan_font_ident();
+ k = cur_val;
+ if (k == null_font)
+ pdf_error("font", "invalid font identifier");
+ scan_pdf_ext_toks();
+ set_pdf_font_attr(k, tokens_to_string(def_ref));
+ if (str_length(pdf_font_attr(k)) == 0) {
+ flush_str((str_ptr - 1)); /* from |tokens_to_string| */
+ set_pdf_font_attr(k, 0);
+ }
+ break;
+ case pdf_font_expand_code:
+ /* Implement \.{\\pdffontexpand} */
+ read_expand_font();
+ break;
+ case pdf_include_chars_code:
+ /* Implement \.{\\pdfincludechars} */
+ check_o_mode(pdf, "\\pdfincludechars", 1 << OMODE_PDF, false);
+ pdf_include_chars(pdf);
+ break;
+ case pdf_info_code:
+ /* Implement \.{\\pdfinfo} */
+ check_o_mode(pdf, "\\pdfinfo", 1 << OMODE_PDF, false);
+ scan_pdf_ext_toks();
+ pdf_info_toks = concat_tokens(pdf_info_toks, def_ref);
+ break;
+ case pdf_literal_node:
+ /* Implement \.{\\pdfliteral} */
+ check_o_mode(pdf, "\\pdfliteral", 1 << OMODE_PDF, false);
+ new_whatsit(pdf_literal_node);
+ if (scan_keyword("direct"))
+ set_pdf_literal_mode(tail, direct_always);
+ else if (scan_keyword("page"))
+ set_pdf_literal_mode(tail, direct_page);
+ else
+ set_pdf_literal_mode(tail, set_origin);
+ scan_pdf_ext_toks();
+ set_pdf_literal_type(tail, normal);
+ set_pdf_literal_data(tail, def_ref);
+ break;
+ case pdf_colorstack_node:
+ /* Implement \.{\\pdfcolorstack} */
+ check_o_mode(pdf, "\\pdfcolorstack", 1 << OMODE_PDF, false);
+ /* Scan and check the stack number and store in |cur_val| */
+ scan_int();
+ if (cur_val >= colorstackused()) {
+ print_err("Unknown color stack number ");
+ print_int(cur_val);
+ help3
+ ("Allocate and initialize a color stack with \\pdfcolorstackinit.",
+ "I'll use default color stack 0 here.",
+ "Proceed, with fingers crossed.");
+ error();
+ cur_val = 0;
+ }
+ if (cur_val < 0) {
+ print_err("Invalid negative color stack number");
+ help2("I'll use default color stack 0 here.",
+ "Proceed, with fingers crossed.");
+ error();
+ cur_val = 0;
+ }
+ if (scan_keyword("set"))
+ i = colorstack_set;
+ else if (scan_keyword("push"))
+ i = colorstack_push;
+ else if (scan_keyword("pop"))
+ i = colorstack_pop;
+ else if (scan_keyword("current"))
+ i = colorstack_current;
+ else
+ i = -1; /* error */
+
+ if (i >= 0) {
+ new_whatsit(pdf_colorstack_node);
+ set_pdf_colorstack_stack(tail, cur_val);
+ set_pdf_colorstack_cmd(tail, i);
+ set_pdf_colorstack_data(tail, null);
+ if (i <= colorstack_data) {
+ scan_pdf_ext_toks();
+ set_pdf_colorstack_data(tail, def_ref);
+ }
+ } else {
+ print_err("Color stack action is missing");
+ help3("The expected actions for \\pdfcolorstack:",
+ " set, push, pop, current",
+ "I'll ignore the color stack command.");
+ error();
+ }
+ break;
+ case pdf_setmatrix_node:
+ /* Implement \.{\\pdfsetmatrix} */
+ check_o_mode(pdf, "\\pdfsetmatrix", 1 << OMODE_PDF, false);
+ new_whatsit(pdf_setmatrix_node);
+ scan_pdf_ext_toks();
+ set_pdf_setmatrix_data(tail, def_ref);
+ break;
+ case pdf_save_node:
+ /* Implement \.{\\pdfsave} */
+ check_o_mode(pdf, "\\pdfsave", 1 << OMODE_PDF, false);
+ new_whatsit(pdf_save_node);
+ break;
+ case pdf_restore_node:
+ /* Implement \.{\\pdfrestore} */
+ check_o_mode(pdf, "\\pdfrestore", 1 << OMODE_PDF, false);
+ new_whatsit(pdf_restore_node);
+ break;
+ case pdf_map_file_code:
+ /* Implement \.{\\pdfmapfile} */
+ check_o_mode(pdf, "\\pdfmapfile", 1 << OMODE_PDF, false);
+ scan_pdf_ext_toks();
+ pdfmapfile(def_ref);
+ delete_token_ref(def_ref);
+ break;
+ case pdf_map_line_code:
+ /* Implement \.{\\pdfmapline} */
+ check_o_mode(pdf, "\\pdfmapline", 1 << OMODE_PDF, false);
+ scan_pdf_ext_toks();
+ pdfmapline(def_ref);
+ delete_token_ref(def_ref);
+ break;
+ case pdf_names_code:
+ /* Implement \.{\\pdfnames} */
+ check_o_mode(pdf, "\\pdfnames", 1 << OMODE_PDF, false);
+ scan_pdf_ext_toks();
+ pdf_names_toks = concat_tokens(pdf_names_toks, def_ref);
+ break;
+ case pdf_obj_code:
+ /* Implement \.{\\pdfobj} */
+ check_o_mode(pdf, "\\pdfobj", 1 << OMODE_PDF, false);
+ scan_obj(pdf);
+ break;
+ case pdf_outline_code:
+ /* Implement \.{\\pdfoutline} */
+ check_o_mode(pdf, "\\pdfoutline", 1 << OMODE_PDF, true);
+ scan_pdfoutline(pdf);
+ break;
+ case pdf_refobj_node:
+ /* Implement \.{\\pdfrefobj} */
+ check_o_mode(pdf, "\\pdfrefobj", 1 << OMODE_PDF, false);
+ scan_refobj(pdf);
+ break;
+ case pdf_refxform_node:
+ /* Implement \.{\\pdfrefxform} */
+ check_o_mode(pdf, "\\pdfrefxform", 1 << OMODE_PDF, false);
+ scan_pdfrefxform(pdf);
+ break;
+ case pdf_refximage_node:
+ /* Implement \.{\\pdfrefximage} */
+ check_o_mode(pdf, "\\pdfrefximage", 1 << OMODE_PDF, false);
+ scan_pdfrefximage(pdf);
+ break;
+ case pdf_save_pos_node:
+ /* Implement \.{\\pdfsavepos} */
+ new_whatsit(pdf_save_pos_node);
+ break;
+ case pdf_start_link_node:
+ /* Implement \.{\\pdfstartlink} */
+ check_o_mode(pdf, "\\pdfstartlink", 1 << OMODE_PDF, false);
+ scan_startlink(pdf);
+ break;
+ case pdf_start_thread_node:
+ /* Implement \.{\\pdfstartthread} */
+ check_o_mode(pdf, "\\pdfstartthread", 1 << OMODE_PDF, false);
+ new_annot_whatsit(pdf_start_thread_node);
+ scan_thread_id();
+ break;
+ case pdf_thread_node:
+ /* Implement \.{\\pdfthread} */
+ check_o_mode(pdf, "\\pdfthread", 1 << OMODE_PDF, false);
+ new_annot_whatsit(pdf_thread_node);
+ scan_thread_id();
+ break;
+ case pdf_trailer_code:
+ /* Implement \.{\\pdftrailer} */
+ check_o_mode(pdf, "\\pdftrailer", 1 << OMODE_PDF, false);
+ scan_pdf_ext_toks();
+ pdf_trailer_toks = concat_tokens(pdf_trailer_toks, def_ref);
+ break;
+ case pdf_xform_code:
+ /* Implement \.{\\pdfxform} */
+ check_o_mode(pdf, "\\pdfxform", 1 << OMODE_PDF, false);
+ scan_pdfxform(pdf);
+ break;
+ case pdf_ximage_code:
+ /* Implement \.{\\pdfximage} */
+ check_o_mode(pdf, "\\pdfximage", 1 << OMODE_PDF, false);
+ /* png, jpeg, and pdf image handling depends on this done so early: */
+ fix_pdf_minorversion(pdf);
+ scan_pdfximage(pdf);
+ break;
+ case save_cat_code_table_code:
+ /* Implement \.{\\savecatcodetable} */
+ scan_int();
+ if ((cur_val < 0) || (cur_val > 0xFFFF)) {
+ print_err("Invalid \\catcode table");
+ help1("All \\catcode table ids must be between 0 and 0xFFFF");
+ error();
+ } else {
+ if (cur_val == cat_code_table) {
+ print_err("Invalid \\catcode table");
+ help1("You cannot overwrite the current \\catcode table");
+ error();
+ } else {
+ copy_cat_codes(cat_code_table, cur_val);
+ }
+ }
+ break;
+ case init_cat_code_table_code:
+ /* Implement \.{\\initcatcodetable} */
+ scan_int();
+ if ((cur_val < 0) || (cur_val > 0xFFFF)) {
+ print_err("Invalid \\catcode table");
+ help1("All \\catcode table ids must be between 0 and 0xFFFF");
+ error();
+ } else {
+ if (cur_val == cat_code_table) {
+ print_err("Invalid \\catcode table");
+ help1("You cannot overwrite the current \\catcode table");
+ error();
+ } else {
+ initex_cat_codes(cur_val);
+ }
+ }
+ break;
+ case set_random_seed_code:
+ /* Implement \.{\\pdfsetrandomseed} */
+ /* Negative random seed values are silently converted to positive ones */
+ scan_int();
+ if (cur_val < 0)
+ negate(cur_val);
+ random_seed = cur_val;
+ init_randoms(random_seed);
+ break;
+ case pdf_glyph_to_unicode_code:
+ /* Implement \.{\\pdfglyphtounicode} */
+ glyph_to_unicode();
+ break;
+ case late_lua_node:
+ /* Implement \.{\\latelua} */
+ new_whatsit(late_lua_node);
+ late_lua_name(tail) = scan_lua_state();
+ (void) scan_toks(false, false);
+ late_lua_data(tail) = def_ref;
+ break;
+ default:
+ confusion("ext1");
+ break;
+ }
+}
+
+
+@ Here is a subroutine that creates a whatsit node having a given |subtype|
+and a given number of words. It initializes only the first word of the whatsit,
+and appends it to the current list.
+
+@c
+void new_whatsit(int s)
+{
+ halfword p; /* the new node */
+ p = new_node(whatsit_node, s);
+ couple_nodes(tail, p);
+ tail = p;
+}
+
+
+@ The next subroutine uses |cur_chr| to decide what sort of whatsit is
+involved, and also inserts a |write_stream| number.
+
+@c
+void new_write_whatsit(int w)
+{
+ new_whatsit(cur_chr);
+ if (w != write_node_size) {
+ scan_four_bit_int();
+ } else {
+ scan_int();
+ if (cur_val < 0)
+ cur_val = 17;
+ else if ((cur_val > 15) && (cur_val != 18))
+ cur_val = 16;
+ }
+ write_stream(tail) = cur_val;
+}
+
+
+@ We have to check whether \.{\\pdfoutput} is set for using \pdfTeX{}
+ extensions.
+
+@c
+void scan_pdf_ext_toks(void)
+{
+ (void) scan_toks(false, true); /* like \.{\\special} */
+}
+
+@ We need to check whether the referenced object exists.
+
+finds the node preceding the rightmost node |e|; |s| is some node before |e|
+@c
+halfword prev_rightmost(halfword s, halfword e)
+{
+ halfword p = s;
+ if (p == null)
+ return null;
+ while (vlink(p) != e) {
+ p = vlink(p);
+ if (p == null)
+ return null;
+ }
+ return p;
+}
+
+@ \.{\\pdfxform} and \.{\\pdfrefxform} are similiar to \.{\\pdfobj} and
+ \.{\\pdfrefobj}
+
+@c
+int pdf_last_xform;
+
+@ \.{\\pdfximage} and \.{\\pdfrefximage} are similiar to \.{\\pdfxform} and
+ \.{\\pdfrefxform}. As we have to scan |<rule spec>| quite often, it is better
+ have a |rule_node| that holds the most recently scanned |<rule spec>|.
+
+@c
+int pdf_last_ximage;
+int pdf_last_ximage_pages;
+int pdf_last_ximage_colordepth;
+int pdf_last_annot;
+
+@ pdflastlink needs an extra global variable
+
+@c
+int pdf_last_link;
+scaledpos pdf_last_pos = { 0, 0 };
+
+
+@ To implement primitives as \.{\\pdfinfo}, \.{\\pdfcatalog} or
+\.{\\pdfnames} we need to concatenate tokens lists.
+
+@c
+halfword concat_tokens(halfword q, halfword r)
+{ /* concat |q| and |r| and returns the result tokens list */
+ halfword p;
+ if (q == null)
+ return r;
+ p = q;
+ while (token_link(p) != null)
+ p = token_link(p);
+ set_token_link(p, token_link(r));
+ free_avail(r);
+ return q;
+}
+
+@ @c
+int pdf_retval; /* global multi-purpose return value */
+
+@ @c
+halfword make_local_par_node(void)
+/* This function creates a |local_paragraph| node */
+{
+ halfword p, q;
+ p = new_node(whatsit_node, local_par_node);
+ local_pen_inter(p) = local_inter_line_penalty;
+ local_pen_broken(p) = local_broken_penalty;
+ if (local_left_box != null) {
+ q = copy_node_list(local_left_box);
+ local_box_left(p) = q;
+ local_box_left_width(p) = width(local_left_box);
+ }
+ if (local_right_box != null) {
+ q = copy_node_list(local_right_box);
+ local_box_right(p) = q;
+ local_box_right_width(p) = width(local_right_box);
+ }
+ local_par_dir(p) = par_direction;
+ return p;
+}
+
+
+
+@ The \eTeX\ features available in extended mode are grouped into two
+categories: (1)~Some of them are permanently enabled and have no
+semantic effect as long as none of the additional primitives are
+executed. (2)~The remaining \eTeX\ features are optional and can be
+individually enabled and disabled. For each optional feature there is
+an \eTeX\ state variable named \.{\\...state}; the feature is enabled,
+resp.\ disabled by assigning a positive, resp.\ non-positive value to
+that integer.
+
+
+@ In order to handle \.{\\everyeof} we need an array |eof_seen| of
+boolean variables.
+
+@c
+boolean *eof_seen; /* has eof been seen? */
+
+
+@ The |print_group| procedure prints the current level of grouping and
+the name corresponding to |cur_group|.
+
+@c
+void print_group(boolean e)
+{
+ switch (cur_group) {
+ case bottom_level:
+ tprint("bottom level");
+ return;
+ break;
+ case simple_group:
+ case semi_simple_group:
+ if (cur_group == semi_simple_group)
+ tprint("semi ");
+ tprint("simple");
+ break;;
+ case hbox_group:
+ case adjusted_hbox_group:
+ if (cur_group == adjusted_hbox_group)
+ tprint("adjusted ");
+ tprint("hbox");
+ break;
+ case vbox_group:
+ tprint("vbox");
+ break;
+ case vtop_group:
+ tprint("vtop");
+ break;
+ case align_group:
+ case no_align_group:
+ if (cur_group == no_align_group)
+ tprint("no ");
+ tprint("align");
+ break;
+ case output_group:
+ tprint("output");
+ break;
+ case disc_group:
+ tprint("disc");
+ break;
+ case insert_group:
+ tprint("insert");
+ break;
+ case vcenter_group:
+ tprint("vcenter");
+ break;
+ case math_group:
+ case math_choice_group:
+ case math_shift_group:
+ case math_left_group:
+ tprint("math");
+ if (cur_group == math_choice_group)
+ tprint(" choice");
+ else if (cur_group == math_shift_group)
+ tprint(" shift");
+ else if (cur_group == math_left_group)
+ tprint(" left");
+ break;
+ } /* there are no other cases */
+ tprint(" group (level ");
+ print_int(cur_level);
+ print_char(')');
+ if (saved_value(-1) != 0) { /* |saved_line| */
+ if (e)
+ tprint(" entered at line ");
+ else
+ tprint(" at line ");
+ print_int(saved_value(-1)); /* |saved_line| */
+ }
+}
+
+
+@ The |group_trace| procedure is called when a new level of grouping
+begins (|e=false|) or ends (|e=true|) with |saved_value(-1)| containing the
+line number.
+
+@c
+void group_trace(boolean e)
+{
+ begin_diagnostic();
+ print_char('{');
+ if (e)
+ tprint("leaving ");
+ else
+ tprint("entering ");
+ print_group(e);
+ print_char('}');
+ end_diagnostic(false);
+}
+
+@ A group entered (or a conditional started) in one file may end in a
+different file. Such slight anomalies, although perfectly legitimate,
+may cause errors that are difficult to locate. In order to be able to
+give a warning message when such anomalies occur, \eTeX\ uses the
+|grp_stack| and |if_stack| arrays to record the initial |cur_boundary|
+and |cond_ptr| values for each input file.
+
+@c
+save_pointer *grp_stack; /* initial |cur_boundary| */
+halfword *if_stack; /* initial |cond_ptr| */
+
+
+@ When a group ends that was apparently entered in a different input
+file, the |group_warning| procedure is invoked in order to update the
+|grp_stack|. If moreover \.{\\tracingnesting} is positive we want to
+give a warning message. The situation is, however, somewhat complicated
+by two facts: (1)~There may be |grp_stack| elements without a
+corresponding \.{\\input} file or \.{\\scantokens} pseudo file (e.g.,
+error insertions from the terminal); and (2)~the relevant information is
+recorded in the |name_field| of the |input_stack| only loosely
+synchronized with the |in_open| variable indexing |grp_stack|.
+
+@c
+void group_warning(void)
+{
+ int i; /* index into |grp_stack| */
+ boolean w; /* do we need a warning? */
+ base_ptr = input_ptr;
+ input_stack[base_ptr] = cur_input; /* store current state */
+ i = in_open;
+ w = false;
+ while ((grp_stack[i] == cur_boundary) && (i > 0)) {
+ /* Set variable |w| to indicate if this case should be reported */
+ /* This code scans the input stack in order to determine the type of the
+ current input file. */
+ if (tracing_nesting > 0) {
+ while ((input_stack[base_ptr].state_field == token_list) ||
+ (input_stack[base_ptr].index_field > i))
+ decr(base_ptr);
+ if (input_stack[base_ptr].name_field > 17)
+ w = true;
+ }
+
+ grp_stack[i] = save_value(save_ptr);
+ decr(i);
+ }
+ if (w) {
+ tprint_nl("Warning: end of ");
+ print_group(true);
+ tprint(" of a different file");
+ print_ln();
+ if (tracing_nesting > 1)
+ show_context();
+ if (history == spotless)
+ history = warning_issued;
+ }
+}
+
+
+@ When a conditional ends that was apparently started in a different
+input file, the |if_warning| procedure is invoked in order to update the
+|if_stack|. If moreover \.{\\tracingnesting} is positive we want to
+give a warning message (with the same complications as above).
+
+@c
+void if_warning(void)
+{
+ int i; /* index into |if_stack| */
+ boolean w; /* do we need a warning? */
+ base_ptr = input_ptr;
+ input_stack[base_ptr] = cur_input; /* store current state */
+ i = in_open;
+ w = false;
+ while (if_stack[i] == cond_ptr) {
+ /* Set variable |w| to... */
+ if (tracing_nesting > 0) {
+ while ((input_stack[base_ptr].state_field == token_list) ||
+ (input_stack[base_ptr].index_field > i))
+ decr(base_ptr);
+ if (input_stack[base_ptr].name_field > 17)
+ w = true;
+ }
+
+ if_stack[i] = vlink(cond_ptr);
+ decr(i);
+ }
+ if (w) {
+ tprint_nl("Warning: end of ");
+ print_cmd_chr(if_test_cmd, cur_if);
+ print_if_line(if_line);
+ tprint(" of a different file");
+ print_ln();
+ if (tracing_nesting > 1)
+ show_context();
+ if (history == spotless)
+ history = warning_issued;
+ }
+}
+
+
+@ Conversely, the |file_warning| procedure is invoked when a file ends
+and some groups entered or conditionals started while reading from that
+file are still incomplete.
+
+@c
+void file_warning(void)
+{
+ halfword p; /* saved value of |save_ptr| or |cond_ptr| */
+ int l; /* saved value of |cur_level| or |if_limit| */
+ int c; /* saved value of |cur_group| or |cur_if| */
+ int i; /* saved value of |if_line| */
+ p = save_ptr;
+ l = cur_level;
+ c = cur_group;
+ save_ptr = cur_boundary;
+ while (grp_stack[in_open] != save_ptr) {
+ decr(cur_level);
+ tprint_nl("Warning: end of file when ");
+ print_group(true);
+ tprint(" is incomplete");
+ cur_group = save_level(save_ptr);
+ save_ptr = save_value(save_ptr);
+ }
+ save_ptr = p;
+ cur_level = (quarterword) l;
+ cur_group = (group_code) c; /* restore old values */
+ p = cond_ptr;
+ l = if_limit;
+ c = cur_if;
+ i = if_line;
+ while (if_stack[in_open] != cond_ptr) {
+ tprint_nl("Warning: end of file when ");
+ print_cmd_chr(if_test_cmd, cur_if);
+ if (if_limit == fi_code)
+ tprint_esc("else");
+ print_if_line(if_line);
+ tprint(" is incomplete");
+ if_line = if_line_field(cond_ptr);
+ cur_if = if_limit_subtype(cond_ptr);
+ if_limit = if_limit_type(cond_ptr);
+ cond_ptr = vlink(cond_ptr);
+ }
+ cond_ptr = p;
+ if_limit = l;
+ cur_if = c;
+ if_line = i; /* restore old values */
+ print_ln();
+ if (tracing_nesting > 1)
+ show_context();
+ if (history == spotless)
+ history = warning_issued;
+}
+
+@ @c
+halfword last_line_fill; /* the |par_fill_skip| glue node of the new paragraph */
+
+
+@ The lua interface needs some extra functions. The functions
+themselves are quite boring, but they are handy because otherwise this
+internal stuff has to be accessed from C directly, where lots of the
+defines are not available.
+
+@c
+#define get_tex_dimen_register(j) dimen(j)
+#define get_tex_skip_register(j) skip(j)
+#define get_tex_count_register(j) count(j)
+#define get_tex_attribute_register(j) attribute(j)
+#define get_tex_box_register(j) box(j)
+
+int set_tex_dimen_register(int j, scaled v)
+{
+ int a; /* return non-nil for error */
+ if (global_defs > 0)
+ a = 4;
+ else
+ a = 0;
+ word_define(j + scaled_base, v);
+ return 0;
+}
+
+int set_tex_skip_register(int j, halfword v)
+{
+ int a; /* return non-nil for error */
+ if (global_defs > 0)
+ a = 4;
+ else
+ a = 0;
+ if (type(v) != glue_spec_node)
+ return 1;
+ word_define(j + skip_base, v);
+ return 0;
+}
+
+int set_tex_count_register(int j, scaled v)
+{
+ int a; /* return non-nil for error */
+ if (global_defs > 0)
+ a = 4;
+ else
+ a = 0;
+ word_define(j + count_base, v);
+ return 0;
+}
+
+int set_tex_box_register(int j, scaled v)
+{
+ int a; /* return non-nil for error */
+ if (global_defs > 0)
+ a = 4;
+ else
+ a = 0;
+ define(j + box_base, box_ref_cmd, v);
+ return 0;
+}
+
+int set_tex_attribute_register(int j, scaled v)
+{
+ int a; /* return non-nil for error */
+ if (global_defs > 0)
+ a = 4;
+ else
+ a = 0;
+ if (j > max_used_attr)
+ max_used_attr = j;
+ attr_list_cache = cache_disabled;
+ word_define(j + attribute_base, v);
+ return 0;
+}
+
+int get_tex_toks_register(int j)
+{
+ str_number s;
+ s = get_nullstr();
+ if (toks(j) != null) {
+ s = tokens_to_string(toks(j));
+ }
+ return s;
+}
+
+int set_tex_toks_register(int j, lstring s)
+{
+ halfword ref;
+ int a;
+ ref = get_avail();
+ (void) str_toks(s);
+ set_token_ref_count(ref, 0);
+ set_token_link(ref, token_link(temp_token_head));
+ if (global_defs > 0)
+ a = 4;
+ else
+ a = 0;
+ define(j + toks_base, call_cmd, ref);
+ return 0;
+}
+
+scaled get_tex_box_width(int j)
+{
+ halfword q = box(j);
+ if (q != null)
+ return width(q);
+ return 0;
+}
+
+int set_tex_box_width(int j, scaled v)
+{
+ halfword q = box(j);
+ if (q == null)
+ return 1;
+ width(q) = v;
+ return 0;
+}
+
+scaled get_tex_box_height(int j)
+{
+ halfword q = box(j);
+ if (q != null)
+ return height(q);
+ return 0;
+}
+
+int set_tex_box_height(int j, scaled v)
+{
+ halfword q = box(j);
+ if (q == null)
+ return 1;
+ height(q) = v;
+ return 0;
+}
+
+
+scaled get_tex_box_depth(int j)
+{
+ halfword q = box(j);
+ if (q != null)
+ return depth(q);
+ return 0;
+}
+
+int set_tex_box_depth(int j, scaled v)
+{
+ halfword q = box(j);
+ if (q == null)
+ return 1;
+ depth(q) = v;
+ return 0;
+}
+
+
+@ This section is devoted to the {\sl Synchronize \TeX nology}
+- or simply {\sl Sync\TeX} - used to synchronize between input and output.
+This section explains how synchronization basics are implemented.
+Before we enter into more technical details,
+let us recall in a few words what is synchronization.
+
+\TeX\ typesetting system clearly separates the input and the output material,
+and synchronization will provide a new link between both that can help
+text editors and viewers to work together.
+More precisely, forwards synchronization is the ability,
+given a location in the input source file,
+to find what is the corresponding place in the output.
+Backwards synchronization just performs the opposite:
+given a location in the output,
+retrieve the corresponding material in the input source file.
+
+For better code management and maintainance, we adopt a naming convention.
+Throughout this program, code related to the {\sl Synchronize \TeX nology} is tagged
+with the ``{\sl synctex}'' key word. Any code extract where {\sl Sync\TeX} plays
+its part, either explicitly or implicitly, (should) contain the string ``{\sl synctex}''.
+This naming convention also holds for external files.
+Moreover, all the code related to {\sl Sync\TeX} is gathered in this section,
+except the definitions.
+
+Enabling synchronization should be performed from the command line,
+|synctexoption| is used for that purpose.
+This global integer variable is declared here but it is not used here.
+This is just a placeholder where the command line controller will put
+the {\sl Sync\TeX} related options, and the {\sl Sync\TeX} controller will read them.
+
+@c
+int synctexoption;
+
+
+@ A convenient primitive is provided:
+\.{\\synctex=1} in the input source file enables synchronization whereas
+\.{\\synctex=0} disables it.
+Its memory address is |synctex_code|.
+It is initialized by the {\sl Sync\TeX} controller to the command-line option if given.
+The controller may filter some reserved bits.
+
+In order to give the {\sl Sync\TeX} controller read and write access to
+the contents of the \.{\\synctex} primitive, we declare |synctexoffset|,
+such that |mem[synctexoffset]| and \.{\\synctex} correspond to
+the same memory storage. |synctexoffset| is initialized to
+the correct value when quite everything is initialized.
+
+@c
+int synctexoffset; /* holds the true value of |synctex_code| */
+
+
+@ Synchronization is achieved with the help of an auxiliary file named
+`\.{{\sl jobname}.synctex}' ({\sl jobname} is the contents of the
+\.{\\jobname} macro), where a {\sl Sync\TeX} controller implemented
+in the external |synctex.c| file will store geometrical information.
+This {\sl Sync\TeX} controller will take care of every technical details
+concerning the {\sl Sync\TeX} file, we will only focus on the messages
+the controller will receive from the \TeX\ program.
+
+The most accurate synchronization information should allow to map
+any character of the input source file to the corresponding location
+in the output, if relevant.
+Ideally, the synchronization information of the input material consists of
+the file name, the line and column numbers of every character.
+The synchronization information in the output is simply the page number and
+either point coordinates, or box dimensions and position.
+The problem is that the mapping between these informations is only known at
+ship out time, which means that we must keep track of the input
+synchronization information until the pages ship out.
+
+As \TeX\ only knows about file names and line numbers,
+but forgets the column numbers, we only consider a
+restricted input synchronization information called {\sl Sync\TeX\ information}.
+It consists of a unique file name identifier, the {\sl Sync\TeX\ file tag},
+and the line number.
+
+Keeping track of such information,
+should be different whether characters or nodes are involved.
+Actually, only certain nodes are involved in {\sl Sync\TeX},
+we call them {\sl synchronized nodes}.
+Synchronized nodes store the {\sl Sync\TeX} information in their last two words:
+the first one contains a {\sl Sync\TeX\ file tag} uniquely identifying
+the input file, and the second one contains the current line number,
+as returned by the \.{\\inputlineno} primitive.
+The |synctex_field_size| macro contains the necessary size to store
+the {\sl Sync\TeX} information in a node.
+
+When declaring the size of a new node, it is recommanded to use the following
+convention: if the node is synchronized, use a definition similar to
+|my_synchronized_node_size|={\sl xxx}+|synctex_field_size|.
+Moreover, one should expect that the {\sl Sync\TeX} information is always stored
+in the last two words of a synchronized node.
+
+By default, every node with a sufficiently big size is initialized
+at creation time in the |get_node| routine with the current
+{\sl Sync\TeX} information, whether or not the node is synchronized.
+One purpose is to set this information very early in order to minimize code
+dependencies, including forthcoming extensions.
+Another purpose is to avoid the assumption that every node type has a dedicated getter,
+where initialization should take place. Actually, it appears that some nodes are created
+using directly the |get_node| routine and not the dedicated constructor.
+And finally, initializing the node at only one place is less error prone.
+
+Instead of storing the input file name, it is better to store just an identifier.
+Each time \TeX\ opens a new file, it notifies the {\sl Sync\TeX} controller with
+a |synctex_start_input| message.
+This controller will create a new {\sl Sync\TeX} file tag and
+will update the current input state record accordingly.
+If the input comes from the terminal or a pseudo file, the |synctex_tag| is set to 0.
+It results in automatically disabling synchronization for material
+input from the terminal or pseudo files.
+
+
+Synchronized nodes are boxes, math, kern and glue nodes.
+Other nodes should be synchronized too, in particular math noads.
+\TeX\ assumes that math, kern and glue nodes have the same size,
+this is why both are synchronized.
+{\sl In fine}, only horizontal lists are really used in {\sl Sync\TeX},
+but all box nodes are considered the same with respect to synchronization,
+because a box node type is allowed to change at execution time.
+
+{\sl Nota Bene:}
+The {\sl Sync\TeX} code is very close to the memory model.
+It is not connected to any other part of the code,
+except for memory management. It is possible to neutralize the {\sl Sync\TeX} code
+rather simply. The first step is to define a null |synctex_field_size|.
+The second step is to comment out the code in ``Initialize bigger nodes...'' and every
+``Copy ... {\sl Sync\TeX} information''.
+The last step will be to comment out the |synctex_tag_field| related code in the
+definition of |synctex_tag| and the various ``Prepare ... {\sl Sync\TeX} information''.
+Then all the remaining code should be just harmless.
+The resulting program would behave exactly the same as if absolutely no {\sl Sync\TeX}
+related code was there, including memory management.
+Of course, all this assumes that {\sl Sync\TeX} is turned off from the command line.
+@^synctex@>
+@^synchronization@>
+
+
+@ Here are extra variables for Web2c. (This numbering of the
+system-dependent section allows easy integration of Web2c and e-\TeX, etc.)
+@^<system dependencies@>
+
+@c
+pool_pointer edit_name_start; /* where the filename to switch to starts */
+int edit_name_length, edit_line; /* what line to start editing at */
+int ipcon; /* level of IPC action, 0 for none [default] */
+boolean stop_at_space; /* whether |more_name| returns false for space */
+
+@ The |edit_name_start| will be set to point into |str_pool| somewhere after
+its beginning if \TeX\ is supposed to switch to an editor on exit.
+
+@c
+int shellenabledp;
+int restrictedshell;
+char *output_comment;
+
+@ Are we printing extra info as we read the format file?
+
+@c
+boolean debug_format_file;