diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex/extensions.c')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/extensions.c | 1329 |
1 files changed, 1329 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/extensions.c b/Build/source/texk/web2c/luatexdir/tex/extensions.c new file mode 100644 index 00000000000..d5914db8a67 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/extensions.c @@ -0,0 +1,1329 @@ +/* + +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/>. + +*/ + +#include "ptexlib.h" + +#define mode mode_par +#define tail tail_par +#define head head_par +#define dir_save dirs_par + +/*tex + + 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. + + 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|. + +*/ + +alpha_file write_file[last_file_selector+1]; +halfword write_file_mode[last_file_selector+1]; +halfword write_file_translation[last_file_selector+1]; +boolean write_open[last_file_selector+1]; + +scaled neg_wd; +scaled pos_wd; +scaled neg_ht; + +/*tex + + The variable |write_loc| just introduced is used to provide an appropriate + error message in case of ``runaway'' write texts. + +*/ + +/*tex The |eqtb| address of \.{\\write}: */ + +halfword write_loc; + +/*tex + + When an |extension| command occurs in |main_control|, in any mode, the + |do_extension| routine is called. + +*/ + +int last_saved_image_index ; +int last_saved_image_pages ; +int last_saved_box_index ; +scaledpos last_position = { 0, 0 }; + +static void do_extension_dvi(int immediate) +{ + if (scan_keyword("literal")) { + new_whatsit(special_node); + write_stream(tail) = null; + scan_toks(false, true); + write_tokens(tail) = def_ref; + } else { + tex_error("unexpected use of \\dviextension",null); + } +} + +static void do_extension_pdf(int immediate) +{ + int i; + if (scan_keyword("literal")) { + 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 if (scan_keyword("text")) + set_pdf_literal_mode(tail, direct_text); + else if (scan_keyword("raw")) + set_pdf_literal_mode(tail, direct_raw); + else if (scan_keyword("origin")) + set_pdf_literal_mode(tail, set_origin); + else + set_pdf_literal_mode(tail, set_origin); + scan_toks(false, true); + set_pdf_literal_type(tail, normal); + set_pdf_literal_data(tail, def_ref); + } else if (scan_keyword("dest")) { + scan_pdfdest(static_pdf); + } else if (scan_keyword("annot")) { + scan_annot(static_pdf); + } else if (scan_keyword("save")) { + new_whatsit(pdf_save_node); + } else if (scan_keyword("restore")) { + new_whatsit(pdf_restore_node); + } else if (scan_keyword("setmatrix")) { + new_whatsit(pdf_setmatrix_node); + scan_toks(false, true); + set_pdf_setmatrix_data(tail, def_ref); + } else if (scan_keyword("obj")) { + scan_obj(static_pdf); + if (immediate) { + if (obj_data_ptr(static_pdf, pdf_last_obj) == 0) { + /*tex This object has not been initialized yet. */ + normal_error("pdf backend","\\pdfextension obj 'reserveobjnum' cannot be used with \\immediate"); + } + pdf_write_obj(static_pdf, pdf_last_obj); + } + } else if (scan_keyword("refobj")) { + scan_refobj(static_pdf); + } else if (scan_keyword("colorstack")) { + scan_int(); + if (cur_val >= colorstackused()) { + print_err("Unknown color stack number "); + print_int(cur_val); + help3( + "Allocate and initialize a color stack with \\pdfextension colorstackinit.", + "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; + 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_toks(false, true); + set_pdf_colorstack_data(tail, def_ref); + } + } else { + print_err("Color stack action is missing"); + help3( + "The expected actions for \\pdfextension colorstack:", + " set, push, pop, current", + "I'll ignore the color stack command." + ); + error(); + } + } else if (scan_keyword("startlink")) { + scan_startlink(static_pdf); + } else if (scan_keyword("endlink")) { + if (abs(mode) == vmode) + normal_error("pdf backend", "\\pdfextension endlink cannot be used in vertical mode"); + new_whatsit(pdf_end_link_node); + } else if (scan_keyword("startthread")) { + new_annot_whatsit(pdf_start_thread_node); + scan_thread_id(); + } else if (scan_keyword("endthread")) { + new_whatsit(pdf_end_thread_node); + } else if (scan_keyword("thread")) { + new_annot_whatsit(pdf_thread_node); + scan_thread_id(); + } else if (scan_keyword("outline")) { + scan_pdfoutline(static_pdf); + } else if (scan_keyword("glyphtounicode")) { + glyph_to_unicode(); + } else if (scan_keyword("catalog")) { + scan_pdfcatalog(static_pdf); + } else if (scan_keyword("fontattr")) { + /*tex + + 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 font attr to a string containing a single + zero, as that would be nonsensical in the PDF output. + + */ + scan_font_ident(); + i = cur_val; + if (i == null_font) + normal_error("pdf backend", "invalid font identifier"); + scan_toks(false, true); + set_pdf_font_attr(i, tokens_to_string(def_ref)); + if (str_length(pdf_font_attr(i)) == 0) { + /*tex From |tokens_to_string|. */ + flush_str((str_ptr - 1)); + set_pdf_font_attr(i, 0); + } + } else if (scan_keyword("mapfile")) { + scan_toks(false, true); + pdfmapfile(def_ref); + delete_token_ref(def_ref); + } else if (scan_keyword("mapline")) { + scan_toks(false, true); + pdfmapline(def_ref); + delete_token_ref(def_ref); + } else if (scan_keyword("includechars")) { + pdf_include_chars(static_pdf); + } else if (scan_keyword("info")) { + scan_toks(false, true); + pdf_info_toks = concat_tokens(pdf_info_toks, def_ref); + } else if (scan_keyword("names")) { + scan_toks(false, true); + pdf_names_toks = concat_tokens(pdf_names_toks, def_ref); + } else if (scan_keyword("trailer")) { + scan_toks(false, true); + pdf_trailer_toks = concat_tokens(pdf_trailer_toks, def_ref); + } else { + tex_error("unexpected use of \\pdfextension",null); + } +} + +static void do_resource_dvi(int immediate, int code) +{ + /*tex Nothing is done here. */ +} + +static void do_resource_pdf(int immediate, int code) +{ + switch (code) { + case use_box_resource_code: + scan_pdfrefxform(static_pdf); + break; + case use_image_resource_code: + scan_pdfrefximage(static_pdf); + break; + case save_box_resource_code: + scan_pdfxform(static_pdf); + if (immediate) { + pdf_cur_form = last_saved_box_index; + ship_out(static_pdf, obj_xform_box(static_pdf, last_saved_box_index), SHIPPING_FORM); + } + break; + case save_image_resource_code: + scan_pdfximage(static_pdf); + if (immediate) { + pdf_write_image(static_pdf, last_saved_image_index); + } + break; + } +} + +/*tex + + Ad 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}'. + +*/ + +/*tex + + The extensions are backend related. The next subroutine uses |cur_chr| to + decide what sort of whatsit is involved, and also inserts a |write_stream| + number. + +*/ + +static void new_write_whatsit(int w, int check) +{ + new_whatsit(cur_chr); + if (check) { + /*tex So we check with open and close. */ + scan_limited_int(last_file_selector,NULL); + } else { + /*tex But we're tolerant with the rest. */ + scan_int(); + if (cur_val < 0) + cur_val = term_only; + else if (cur_val > last_file_selector) { + cur_val = term_and_log; + } + } + write_stream(tail) = cur_val; +} + +void do_extension(int immediate) +{ + /*tex An all-purpose pointer. */ + halfword p; + if (cur_cmd == extension_cmd) { + /*tex These have their own range starting at 0. */ + switch (cur_chr) { + case open_code: + p = tail; + new_write_whatsit(open_node_size,1); + scan_optional_equals(); + scan_file_name(); + open_name(tail) = cur_name; + open_area(tail) = cur_area; + open_ext(tail) = cur_ext; + if (immediate) { + wrapup_leader(tail); + flush_node_list(tail); + tail = p; + vlink(p) = null; + } + break; + case write_code: + /*tex + + 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. + + */ + p = tail; + new_write_whatsit(write_node_size,0); + cur_cs = write_stream(tail); + scan_toks(false, false); + write_tokens(tail) = def_ref; + if (immediate) { + wrapup_leader(tail); + flush_node_list(tail); + tail = p; + vlink(p) = null; + } + break; + case close_code: + p = tail; + new_write_whatsit(close_node_size,1); + write_tokens(tail) = null; + if (immediate) { + wrapup_leader(tail); + flush_node_list(tail); + tail = p; + vlink(p) = null; + } + break; + case special_code: + /*tex + + 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: + get_x_token(); + do_extension(1); + break; + case end_local_code: + if (tracing_nesting_par > 2) { + local_control_message("leaving token scanner"); + } + end_local_control(); + break; + case use_box_resource_code: + case use_image_resource_code: + case save_box_resource_code: + case save_image_resource_code: + switch (get_o_mode()) { + case OMODE_DVI: + do_resource_dvi(immediate,cur_chr); + break; + case OMODE_PDF: + do_resource_pdf(immediate,cur_chr); + break; + default: + break; + } + break; + /*tex Backend extensions have their own range starting at 32. */ + case dvi_extension_code: + if (get_o_mode() == OMODE_DVI) + do_extension_dvi(immediate); + break; + case pdf_extension_code: + if (get_o_mode() == OMODE_PDF) + do_extension_pdf(immediate); + break; + /*tex Done. */ + default: + if (immediate) { + back_input(); + } else { + confusion("invalid extension"); + } + break; + } + } else { + /*tex No extension command, quite certainly following |\immediate|. */ + back_input(); + } +} + +/*tex + + 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. + +*/ + +void new_whatsit(int s) +{ + halfword p = new_node(whatsit_node, s); + couple_nodes(tail, p); + tail = p; +} + +/*tex + + The final line of this routine is slightly subtle; at least, the author + didn't think about it until getting burnt! There is a used-up token list on + the stack, namely the one that contained |end_write_token|. (We insert this + artificial `\.{\\endwrite}' to prevent runaways, as explained above.) If it + were not removed, and if there were numerous writes on a single page, the + stack would overflow. + +*/ + +void expand_macros_in_tokenlist(halfword p) +{ + int old_mode; + pointer q = get_avail(); + pointer r = get_avail(); + token_info(q) = right_brace_token + '}'; + token_link(q) = r; + token_info(r) = end_write_token; + begin_token_list(q, inserted); + begin_token_list(write_tokens(p), write_text); + q = get_avail(); + token_info(q) = left_brace_token + '{'; + begin_token_list(q, inserted); + /*tex + + Now we're ready to scan `\.\{$\langle\,$token list$\,\rangle$\.{\} + \\endwrite}'. + + */ + old_mode = mode; + mode = 0; + /*tex + + Disable \.{\\prevdepth}, \.{\\spacefactor}, \.{\\lastskip}, + \.{\\prevgraf}. + + */ + cur_cs = write_loc; + /*tex Expand macros, etc. */ + q = scan_toks(false, true); + get_token(); + if (cur_tok != end_write_token) { + /*tex Recover from an unbalanced write command */ + const char *hlp[] = { + "On this page there's a \\write with fewer real {'s than }'s.", + "I can't handle that very well; good luck.", + NULL + }; + tex_error("Unbalanced write command", hlp); + do { + get_token(); + } while (cur_tok != end_write_token); + } + mode = old_mode; + /*tex Conserve stack space. */ + end_token_list(); +} + +void write_out(halfword p) +{ + /*tex holds print |selector| */ + int old_setting; + /*tex write stream number */ + int j; + /*tex line to be written, as a C string */ + char *s, *ss; + int callback_id; + int lua_retval; + expand_macros_in_tokenlist(p); + old_setting = selector; + j = write_stream(p); + if (file_can_be_written(j)) { + selector = j; + } else if ((j == term_only) && (selector == term_and_log)) { + /*tex write to the terminal if file isn't open */ + selector = log_only; + tprint_nl(""); + } else { + tprint_nl(""); + } + s = tokenlist_to_cstring(def_ref, false, NULL); + if (selector < no_print) { + /*tex selector is a file */ + callback_id = callback_defined(process_output_buffer_callback); + if (callback_id > 0) { + /*tex fix up the output buffer using callbacks */ + lua_retval = run_callback(callback_id, "S->S", s, &ss); + if ((lua_retval == true) && (ss != NULL)) { + xfree(s); + s = ss; + } + } + } + tprint(s); + xfree(s); + print_ln(); + flush_list(def_ref); + selector = old_setting; +} + +void finalize_write_files(void) { + int k; + for (k = 0; k <= last_file_selector; k++) { + if (write_open[k]) { + lua_a_close_out(write_file[k]); + } + } +} + +void initialize_write_files(void) { + int k; + for (k = 0; k <= last_file_selector; k++) { + write_open[k] = false; + } +} + +void close_write_file(int id) { + if (write_open[id]) { + lua_a_close_out(write_file[id]); + write_open[id] = false; + } +} + +boolean open_write_file(int id, char *fn) { + if (lua_a_open_out(&(write_file[id]), fn, (id + 1))) { + write_open[id] = true; + return true; + } else { + return false; + } +} + +/*tex + + To implement primitives as \.{\\pdfextension info}, \.{\\pdfextension + catalog} or \.{\\pdfextension names} we need to concatenate tokens lists. + +*/ + +halfword concat_tokens(halfword q, halfword r) +{ + /*tex 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; +} + +/*tex + + 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. + +*/ + +boolean *eof_seen; + +/*tex + + The |print_group| procedure prints the current level of grouping and the name + corresponding to |cur_group|. + +*/ + +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; + } + tprint(" group (level "); + print_int(cur_level); + print_char(')'); + if (saved_value(-1) != 0) { + /*tex |saved_line| */ + if (e) + tprint(" entered at line "); + else + tprint(" at line "); + print_int(saved_value(-1)); + } +} + +/*tex + + 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. + +*/ + +void group_trace(boolean e) +{ + begin_diagnostic(); + print_char('{'); + if (e) + tprint("leaving "); + else + tprint("entering "); + print_group(e); + print_char('}'); + end_diagnostic(false); +} + +/*tex + + 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. + +*/ + +/*tex initial |cur_boundary| */ + +save_pointer *grp_stack; + +/*tex initial |cond_ptr| */ + +halfword *if_stack; + +/*tex + + 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|. + +*/ + +void group_warning(void) +{ + /*tex do we need a warning? */ + boolean w = false; + /*tex index into |grp_stack| */ + int i = in_open; + base_ptr = input_ptr; + /*tex store current state */ + input_stack[base_ptr] = cur_input; + while ((grp_stack[i] == cur_boundary) && (i > 0)) { + /*tex + + 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_par > 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_par > 1) + show_context(); + if (history == spotless) + history = warning_issued; + } +} + +/*tex + + 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). + +*/ + +void if_warning(void) +{ + /*tex Do we need a warning? */ + boolean w = false; + int i = in_open; + base_ptr = input_ptr; + /*tex Store current state. */ + input_stack[base_ptr] = cur_input; + while (if_stack[i] == cond_ptr) { + /*tex Set variable |w| to. */ + if (tracing_nesting_par > 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_par > 1) + show_context(); + if (history == spotless) + history = warning_issued; + } +} + +/*tex + + 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. + +*/ + +void file_warning(void) +{ + /*tex saved value of |save_ptr| or |cond_ptr| */ + halfword p = save_ptr; + /*tex saved value of |cur_level| or |if_limit| */ + int l = cur_level; + /*tex saved value of |cur_group| or |cur_if| */ + int c = cur_group; + /*tex saved value of |if_line| */ + int i; + 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; + /*tex Restore old values. */ + cur_group = (group_code) c; + 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); + } + /*tex restore old values */ + cond_ptr = p; + if_limit = l; + cur_if = c; + if_line = i; + print_ln(); + if (tracing_nesting_par > 1) + show_context(); + if (history == spotless) + history = warning_issued; +} + +/*tex The |par_fill_skip| glue node of the new paragraph. */ + +halfword last_line_fill; + +/*tex + + 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. + +*/ + +#define get_tex_dimen_register(j) dimen(j) +#define get_tex_skip_register(j) skip(j) +#define get_tex_mu_skip_register(j) mu_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) + +/*tex These can now be macros (todo). */ + +int get_tex_extension_count_register(int i) +{ + return (int) int_par(backend_int_base-int_base+i); +} + +void set_tex_extension_count_register(int i, int d) +{ + int_par(backend_int_base-int_base+i) = d; +} + +int get_tex_extension_dimen_register(int i) +{ + return (int) dimen_par(backend_dimen_base-dimen_base+i); +} + +void set_tex_extension_dimen_register(int i, int d) +{ + dimen_par(backend_dimen_base-dimen_base+i) = d; +} + +int get_tex_extension_toks_register(int i) +{ + return equiv(backend_toks_base+i); +} + +int set_tex_dimen_register(int j, scaled v) +{ + int a; + if (global_defs_par > 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; + if (global_defs_par > 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_mu_skip_register(int j, halfword v) +{ + int a; + if (global_defs_par > 0) + a = 4; + else + a = 0; + if (type(v) != glue_spec_node) + return 1; + word_define(j + mu_skip_base, v); + return 0; +} + +int set_tex_count_register(int j, scaled v) +{ + int a; + if (global_defs_par > 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; + if (global_defs_par > 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; + if (global_defs_par > 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 = get_nullstr(); + if (toks(j) != null) { + s = tokens_to_string(toks(j)); + } + return s; +} + +int set_tex_toks_register(int j, lstring s) +{ + int a; + halfword 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_par > 0) + a = 4; + else + a = 0; + define(j + toks_base, call_cmd, ref); + return 0; +} + +int scan_tex_toks_register(int j, int c, lstring s) +{ + int a; + halfword ref = get_avail(); + (void) str_scan_toks(c,s); + set_token_ref_count(ref, 0); + set_token_link(ref, token_link(temp_token_head)); + if (global_defs_par > 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; +} + +/*tex + + 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. + +*/ + +int synctexoption; + +/*tex + + 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. + +*/ + +/*tex Holds the true value of |synctex_code|: */ + +int synctexoffset; + +/*tex + + 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. + + {\em 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. + + Here are extra variables for Web2c. (This numbering of the system-dependent + section allows easy integration of Web2c and e-\TeX, etc.) + +*/ + +/*tex where the filename to switch to starts */ + +pool_pointer edit_name_start; + +/*tex what line to start editing at */ + +int edit_name_length, edit_line; + +/*tex whether |more_name| returns false for space */ + +boolean stop_at_space; + +/*tex + + 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. + +*/ + +int shellenabledp; +int restrictedshell; +char *output_comment; + +/*tex + + Are we printing extra info as we read the format file? + +*/ + +boolean debug_format_file; + +void wrapup_leader(halfword p) +{ + /*tex Do some work that has been queued up for \.{\\write}. */ + if (!doing_leaders) { + int j = write_stream(p); + if (subtype(p) == write_node) { + write_out(p); + } else if (subtype(p) == close_node) { + close_write_file(j); + } else if (valid_write_file(j)) { + char *fn; + close_write_file(j); + cur_name = open_name(p); + cur_area = open_area(p); + cur_ext = open_ext(p); + if (cur_ext == get_nullstr()) + cur_ext = maketexstring(".tex"); + fn = pack_file_name(cur_name, cur_area, cur_ext); + while (! open_write_file(j,fn)) { + fn = prompt_file_name("output file name", ".tex"); + } + } + } +} |