summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/luatexdir/tex/inputstack.w
diff options
context:
space:
mode:
authorTaco Hoekwater <taco@elvenkind.com>2010-04-04 13:55:46 +0000
committerTaco Hoekwater <taco@elvenkind.com>2010-04-04 13:55:46 +0000
commitf372862c42d234710b6057449b46818e63181c90 (patch)
treea9ebec66dd31ac37aed442502ebd1b9fe76245ec /Build/source/texk/web2c/luatexdir/tex/inputstack.w
parent2dad291ab056f30816e0aea61970f38033c2f6a5 (diff)
import luatex 0.60.0 (with small local changes, see luatexdir/ChangeLog)
and do an autoreconf git-svn-id: svn://tug.org/texlive/trunk@17680 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex/inputstack.w')
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/inputstack.w800
1 files changed, 800 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/inputstack.w b/Build/source/texk/web2c/luatexdir/tex/inputstack.w
new file mode 100644
index 00000000000..ecb620fb185
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/inputstack.w
@@ -0,0 +1,800 @@
+% inputstack.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/>.
+
+@ @c
+#include "ptexlib.h"
+
+static const char _svn_version[] =
+ "$Id: inputstack.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/inputstack.w $";
+
+@ @c
+#define end_line_char int_par(end_line_char_code)
+#define error_context_lines int_par(error_context_lines_code)
+
+
+in_state_record *input_stack = NULL;
+int input_ptr = 0; /* first unused location of |input_stack| */
+int max_in_stack = 0; /* largest value of |input_ptr| when pushing */
+in_state_record cur_input; /* the ``top'' input state */
+
+
+int in_open = 0; /* the number of lines in the buffer, less one */
+int open_parens = 0; /* the number of open text files */
+alpha_file *input_file = NULL;
+int line = 0; /* current line number in the current source file */
+int *line_stack = NULL;
+str_number *source_filename_stack = NULL;
+char **full_source_filename_stack = NULL;
+
+
+int scanner_status = 0; /* can a subfile end now? */
+pointer warning_index = null; /* identifier relevant to non-|normal| scanner status */
+pointer def_ref = null; /* reference count of token list being defined */
+
+
+
+@ Here is a procedure that uses |scanner_status| to print a warning message
+when a subfile has ended, and at certain other crucial times:
+
+@c
+void runaway(void)
+{
+ pointer p = null; /* head of runaway list */
+ if (scanner_status > skipping) {
+ switch (scanner_status) {
+ case defining:
+ tprint_nl("Runaway definition");
+ p = def_ref;
+ break;
+ case matching:
+ tprint_nl("Runaway argument");
+ p = temp_token_head;
+ break;
+ case aligning:
+ tprint_nl("Runaway preamble");
+ p = hold_token_head;
+ break;
+ case absorbing:
+ tprint_nl("Runaway text");
+ p = def_ref;
+ break;
+ default: /* there are no other cases */
+ break;
+ }
+ print_char('?');
+ print_ln();
+ show_token_list(token_link(p), null, error_line - 10);
+ }
+}
+
+
+@ The |param_stack| is an auxiliary array used to hold pointers to the token
+lists for parameters at the current level and subsidiary levels of input.
+This stack is maintained with convention (2), and it grows at a different
+rate from the others.
+
+@c
+pointer *param_stack = NULL; /* token list pointers for parameters */
+int param_ptr = 0; /* first unused entry in |param_stack| */
+int max_param_stack = 0; /* largest value of |param_ptr|, will be |<=param_size+9| */
+
+@ The input routines must also interact with the processing of
+\.{\\halign} and \.{\\valign}, since the appearance of tab marks and
+\.{\\cr} in certain places is supposed to trigger the beginning of special
+$v_j$ template text in the scanner. This magic is accomplished by an
+|align_state| variable that is increased by~1 when a `\.{\char'173}' is
+scanned and decreased by~1 when a `\.{\char'175}' is scanned. The |align_state|
+is nonzero during the $u_j$ template, after which it is set to zero; the
+$v_j$ template begins when a tab mark or \.{\\cr} occurs at a time that
+|align_state=0|.
+
+@c
+int align_state = 0; /* group level with respect to current alignment */
+
+
+@ Thus, the ``current input state'' can be very complicated indeed; there
+can be many levels and each level can arise in a variety of ways. The
+|show_context| procedure, which is used by \TeX's error-reporting routine to
+print out the current input state on all levels down to the most recent
+line of characters from an input file, illustrates most of these conventions.
+The global variable |base_ptr| contains the lowest level that was
+displayed by this procedure.
+
+@c
+int base_ptr = 0; /* shallowest level shown by |show_context| */
+
+
+@ The status at each level is indicated by printing two lines, where the first
+line indicates what was read so far and the second line shows what remains
+to be read. The context is cropped, if necessary, so that the first line
+contains at most |half_error_line| characters, and the second contains
+at most |error_line|. Non-current input levels whose |token_type| is
+`|backed_up|' are shown only if they have not been fully read.
+
+@c
+void print_token_list_type(int t)
+{
+ switch (t) {
+ case parameter:
+ tprint_nl("<argument> ");
+ break;
+ case u_template:
+ case v_template:
+ tprint_nl("<template> ");
+ break;
+ case backed_up:
+ if (iloc == null)
+ tprint_nl("<recently read> ");
+ else
+ tprint_nl("<to be read again> ");
+ break;
+ case inserted:
+ tprint_nl("<inserted text> ");
+ break;
+ case macro:
+ print_ln();
+ print_cs(iname);
+ break;
+ case output_text:
+ tprint_nl("<output> ");
+ break;
+ case every_par_text:
+ tprint_nl("<everypar> ");
+ break;
+ case every_math_text:
+ tprint_nl("<everymath> ");
+ break;
+ case every_display_text:
+ tprint_nl("<everydisplay> ");
+ break;
+ case every_hbox_text:
+ tprint_nl("<everyhbox> ");
+ break;
+ case every_vbox_text:
+ tprint_nl("<everyvbox> ");
+ break;
+ case every_job_text:
+ tprint_nl("<everyjob> ");
+ break;
+ case every_cr_text:
+ tprint_nl("<everycr> ");
+ break;
+ case mark_text:
+ tprint_nl("<mark> ");
+ break;
+ case every_eof_text:
+ tprint_nl("<everyeof> ");
+ break;
+ case write_text:
+ tprint_nl("<write> ");
+ break;
+ default:
+ tprint_nl("?");
+ break; /* this should never happen */
+ }
+}
+
+
+@ Here it is necessary to explain a little trick. We don't want to store a long
+string that corresponds to a token list, because that string might take up
+lots of memory; and we are printing during a time when an error message is
+being given, so we dare not do anything that might overflow one of \TeX's
+tables. So `pseudoprinting' is the answer: We enter a mode of printing
+that stores characters into a buffer of length |error_line|, where character
+$k+1$ is placed into \hbox{|trick_buf[k mod error_line]|} if
+|k<trick_count|, otherwise character |k| is dropped. Initially we set
+|tally:=0| and |trick_count:=1000000|; then when we reach the
+point where transition from line 1 to line 2 should occur, we
+set |first_count:=tally| and |trick_count:=@tmax@>(error_line,
+tally+1+error_line-half_error_line)|. At the end of the
+pseudoprinting, the values of |first_count|, |tally|, and
+|trick_count| give us all the information we need to print the two lines,
+and all of the necessary text is in |trick_buf|.
+
+Namely, let |l| be the length of the descriptive information that appears
+on the first line. The length of the context information gathered for that
+line is |k=first_count|, and the length of the context information
+gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|,
+where |h=half_error_line|, we print |trick_buf[0..k-1]| after the
+descriptive information on line~1, and set |n:=l+k|; here |n| is the
+length of line~1. If $l+k>h$, some cropping is necessary, so we set |n:=h|
+and print `\.{...}' followed by
+$$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$
+where subscripts of |trick_buf| are circular modulo |error_line|. The
+second line consists of |n|~spaces followed by |trick_buf[k..(k+m-1)]|,
+unless |n+m>error_line|; in the latter case, further cropping is done.
+This is easier to program than to explain.
+
+
+
+@ The following code sets up the print routines so that they will gather
+the desired information.
+
+@c
+void set_trick_count(void)
+{
+ first_count = tally;
+ trick_count = tally + 1 + error_line - half_error_line;
+ if (trick_count < error_line)
+ trick_count = error_line;
+}
+
+#define begin_pseudoprint() do { \
+ l=tally; tally=0; selector=pseudo; \
+ trick_count=1000000; \
+ } while (0)
+
+#define PSEUDO_PRINT_THE_LINE() do { \
+ begin_pseudoprint(); \
+ if (buffer[ilimit]==end_line_char) j=ilimit; \
+ else j=ilimit+1; /* determine the effective end of the line */ \
+ if (j>0) { \
+ for (i=istart;i<=j-1;i++) { \
+ if (i==iloc) set_trick_count(); \
+ print_char(buffer[i]); \
+ } \
+ } \
+ } while (0)
+
+@ @c
+void show_context(void)
+{ /* prints where the scanner is */
+ int old_setting; /* saved |selector| setting */
+ int nn; /* number of contexts shown so far, less one */
+ boolean bottom_line; /* have we reached the final context to be shown? */
+ int i; /* index into |buffer| */
+ int j; /* end of current line in |buffer| */
+ int l; /* length of descriptive information on line 1 */
+ int m; /* context information gathered for line 2 */
+ int n; /* length of line 1 */
+ int p; /* starting or ending place in |trick_buf| */
+ int q; /* temporary index */
+
+ base_ptr = input_ptr;
+ input_stack[base_ptr] = cur_input;
+ /* store current state */
+ nn = -1;
+ bottom_line = false;
+ while (true) {
+ cur_input = input_stack[base_ptr]; /* enter into the context */
+ if (istate != token_list) {
+ if ((iname > 21) || (base_ptr == 0))
+ bottom_line = true;
+ }
+ if ((base_ptr == input_ptr) || bottom_line
+ || (nn < error_context_lines)) {
+ /* Display the current context */
+ if ((base_ptr == input_ptr) || (istate != token_list) ||
+ (token_type != backed_up) || (iloc != null)) {
+ /* we omit backed-up token lists that have already been read */
+ tally = 0; /* get ready to count characters */
+ old_setting = selector;
+ if (current_ocp_lstack > 0) {
+ tprint_nl("OCP stack ");
+ print_scaled(current_ocp_lstack);
+ tprint(" entry ");
+ print_int(current_ocp_no);
+ tprint(":");
+ PSEUDO_PRINT_THE_LINE();
+ } else if (istate != token_list) {
+ /* Print location of current line */
+ /*
+ This routine should be changed, if necessary, to give the best possible
+ indication of where the current line resides in the input file.
+ For example, on some systems it is best to print both a page and line number.
+ */
+ if (iname <= 17) {
+ if (terminal_input) {
+ if (base_ptr == 0)
+ tprint_nl("<*>");
+ else
+ tprint_nl("<insert> ");
+ } else {
+ tprint_nl("<read ");
+ if (iname == 17)
+ print_char('*');
+ else
+ print_int(iname - 1);
+ print_char('>');
+ };
+ } else if (iindex != in_open) { /* input from a pseudo file */
+ tprint_nl("l.");
+ print_int(line_stack[iindex + 1]);
+ } else {
+ tprint_nl("l.");
+ print_int(line);
+ }
+ print_char(' ');
+ PSEUDO_PRINT_THE_LINE();
+ } else {
+ print_token_list_type(token_type);
+
+ begin_pseudoprint();
+ if (token_type < macro)
+ show_token_list(istart, iloc, 100000);
+ else
+ show_token_list(token_link(istart), iloc, 100000); /* avoid reference count */
+ }
+ selector = old_setting; /* stop pseudoprinting */
+ /* Print two lines using the tricky pseudoprinted information */
+ if (trick_count == 1000000)
+ set_trick_count();
+ /* |set_trick_count| must be performed */
+ if (tally < trick_count)
+ m = tally - first_count;
+ else
+ m = trick_count - first_count; /* context on line 2 */
+ if (l + first_count <= half_error_line) {
+ p = 0;
+ n = l + first_count;
+ } else {
+ tprint("...");
+ p = l + first_count - half_error_line + 3;
+ n = half_error_line;
+ }
+ for (q = p; q <= first_count - 1; q++)
+ print_char(trick_buf[(q % error_line)]);
+ print_ln();
+ for (q = 1; q <= n; q++)
+ print_char(' '); /* print |n| spaces to begin line~2 */
+ if (m + n <= error_line)
+ p = first_count + m;
+ else
+ p = first_count + (error_line - n - 3);
+ for (q = first_count; q <= p - 1; q++)
+ print_char(trick_buf[(q % error_line)]);
+ if (m + n > error_line)
+ tprint("...");
+
+ incr(nn);
+ }
+ } else if (nn == error_context_lines) {
+ tprint_nl("...");
+ incr(nn); /* omitted if |error_context_lines<0| */
+ }
+ if (bottom_line)
+ break;
+ decr(base_ptr);
+ }
+ cur_input = input_stack[input_ptr]; /* restore original state */
+}
+
+
+@ The following subroutines change the input status in commonly needed ways.
+
+First comes |push_input|, which stores the current state and creates a
+new level (having, initially, the same properties as the old).
+
+@c
+/* enter a new input level, save the old */
+
+void push_input(void)
+{
+ if (input_ptr > max_in_stack) {
+ max_in_stack = input_ptr;
+ if (input_ptr == stack_size)
+ overflow("input stack size", (unsigned) stack_size);
+ }
+ input_stack[input_ptr] = cur_input; /* stack the record */
+ nofilter = false;
+ incr(input_ptr);
+}
+
+@
+Here is a procedure that starts a new level of token-list input, given
+a token list |p| and its type |t|. If |t=macro|, the calling routine should
+set |name| and |loc|.
+
+@c
+void begin_token_list(halfword p, quarterword t)
+{
+ push_input();
+ istate = token_list;
+ istart = p;
+ token_type = (unsigned char) t;
+ if (t >= macro) { /* the token list starts with a reference count */
+ add_token_ref(p);
+ if (t == macro) {
+ param_start = param_ptr;
+ } else {
+ iloc = token_link(p);
+ if (int_par(tracing_macros_code) > 1) {
+ begin_diagnostic();
+ tprint_nl("");
+ if (t == mark_text)
+ tprint_esc("mark");
+ else if (t == write_text)
+ tprint_esc("write");
+ else
+ print_cmd_chr(assign_toks_cmd,
+ t - output_text + output_routine_loc);
+ tprint("->");
+ token_show(p);
+ end_diagnostic(false);
+ }
+ }
+ } else {
+ iloc = p;
+ }
+}
+
+
+@ When a token list has been fully scanned, the following computations
+should be done as we leave that level of input. The |token_type| tends
+to be equal to either |backed_up| or |inserted| about 2/3 of the time.
+@^inner loop@>
+
+@c
+void end_token_list(void)
+{ /* leave a token-list input level */
+ if (token_type >= backed_up) { /* token list to be deleted */
+ if (token_type <= inserted) {
+ flush_list(istart);
+ } else {
+ delete_token_ref(istart); /* update reference count */
+ if (token_type == macro) { /* parameters must be flushed */
+ while (param_ptr > param_start) {
+ decr(param_ptr);
+ flush_list(param_stack[param_ptr]);
+ }
+ }
+ }
+ } else if (token_type == u_template) {
+ if (align_state > 500000)
+ align_state = 0;
+ else
+ fatal_error("(interwoven alignment preambles are not allowed)");
+ }
+ pop_input();
+ check_interrupt();
+}
+
+
+@ Sometimes \TeX\ has read too far and wants to ``unscan'' what it has
+seen. The |back_input| procedure takes care of this by putting the token
+just scanned back into the input stream, ready to be read again. This
+procedure can be used only if |cur_tok| represents the token to be
+replaced. Some applications of \TeX\ use this procedure a lot,
+so it has been slightly optimized for speed.
+@^inner loop@>
+
+@c
+void back_input(void)
+{ /* undoes one token of input */
+ halfword p; /* a token list of length one */
+ while ((istate == token_list) && (iloc == null)
+ && (token_type != v_template))
+ end_token_list(); /* conserve stack space */
+ p = get_avail();
+ set_token_info(p, cur_tok);
+ if (cur_tok < right_brace_limit) {
+ if (cur_tok < left_brace_limit)
+ decr(align_state);
+ else
+ incr(align_state);
+ }
+ push_input();
+ istate = token_list;
+ istart = p;
+ token_type = backed_up;
+ iloc = p; /* that was |back_list(p)|, without procedure overhead */
+}
+
+@ Insert token |p| into \TeX's input
+@c
+int reinsert_token(boolean a, halfword pp)
+{
+ halfword t;
+ t = cur_tok;
+ cur_tok = pp;
+ if (a) {
+ halfword p;
+ p = get_avail();
+ set_token_info(p, cur_tok);
+ set_token_link(p, iloc);
+ iloc = p;
+ istart = p;
+ if (cur_tok < right_brace_limit) {
+ if (cur_tok < left_brace_limit)
+ decr(align_state);
+ else
+ incr(align_state);
+ }
+ } else {
+ back_input();
+ a = true; /* etex is always on */
+ }
+ cur_tok = t;
+ return a;
+}
+
+
+@ The |begin_file_reading| procedure starts a new level of input for lines
+of characters to be read from a file, or as an insertion from the
+terminal. It does not take care of opening the file, nor does it set |loc|
+or |limit| or |line|.
+@^system dependencies@>
+
+@c
+void begin_file_reading(void)
+{
+ if (in_open == max_in_open)
+ overflow("text input levels", (unsigned) max_in_open);
+ if (first == buf_size)
+ check_buffer_overflow(first);
+ incr(in_open);
+ push_input();
+ iindex = (unsigned char) in_open;
+ source_filename_stack[iindex] = 0;
+ full_source_filename_stack[iindex] = NULL;
+ eof_seen[iindex] = false;
+ grp_stack[iindex] = cur_boundary;
+ if_stack[iindex] = cond_ptr;
+ line_stack[iindex] = line;
+ istart = first;
+ istate = mid_line;
+ iname = 0; /* |terminal_input| is now |true| */
+ line_catcode_table = DEFAULT_CAT_TABLE;
+ line_partial = false;
+ /* Prepare terminal input {\sl Sync\TeX} information */
+ synctex_tag = 0;
+}
+
+
+@ Conversely, the variables must be downdated when such a level of input
+is finished:
+
+@c
+void end_file_reading(void)
+{
+ first = istart;
+ line = line_stack[iindex];
+ if ((iname >= 18) && (iname <= 20))
+ pseudo_close();
+ else if (iname == 21)
+ luacstring_close(iindex);
+ else if (iname > 17)
+ lua_a_close_in(cur_file, 0); /* forget it */
+ pop_input();
+ decr(in_open);
+}
+
+
+@ In order to keep the stack from overflowing during a long sequence of
+inserted `\.{\\show}' commands, the following routine removes completed
+error-inserted lines from memory.
+
+@c
+void clear_for_error_prompt(void)
+{
+ while ((istate != token_list) && terminal_input
+ && (input_ptr > 0) && (iloc > ilimit))
+ end_file_reading();
+ print_ln();
+ clear_terminal();
+}
+
+@ To get \TeX's whole input mechanism going, we perform the following
+ actions.
+
+@c
+void initialize_inputstack(void)
+{
+ input_ptr = 0;
+ max_in_stack = 0;
+ source_filename_stack[0] = 0;
+
+ full_source_filename_stack[0] = NULL;
+ in_open = 0;
+ open_parens = 0;
+ max_buf_stack = 0;
+
+ grp_stack[0] = 0;
+ if_stack[0] = null;
+ param_ptr = 0;
+ max_param_stack = 0;
+ first = buf_size;
+ do {
+ buffer[first] = 0;
+ decr(first);
+ } while (first != 0);
+ scanner_status = normal;
+ warning_index = null;
+ first = 1;
+ istate = new_line;
+ istart = 1;
+ iindex = 0;
+ line = 0;
+ iname = 0;
+ current_ocp_lstack = 0;
+ current_ocp_no = 0;
+ nofilter = false;
+ force_eof = false;
+ luacstrings = 0;
+ line_catcode_table = DEFAULT_CAT_TABLE;
+ line_partial = false;
+ align_state = 1000000;
+ if (!init_terminal())
+ exit(EXIT_FAILURE); /* |goto final_end|; */
+ ilimit = last;
+ first = last + 1; /* |init_terminal| has set |loc| and |last| */
+}
+
+
+
+
+@ The global variable |pseudo_files| is used to maintain a stack of
+pseudo files. The |pseudo_lines| field of each pseudo file points to
+a linked list of variable size nodes representing lines not yet
+processed: the |subtype| field contains the size of this node,
+all the following words contain ASCII codes.
+
+@c
+halfword pseudo_files; /* stack of pseudo files */
+
+static halfword string_to_pseudo(str_number str, int nl)
+{
+ halfword i, r, q = null;
+ unsigned l, len;
+ four_quarters w;
+ int sz;
+ halfword h = new_node(pseudo_file_node, 0);
+ unsigned char *s = str_string(str);
+ len = (unsigned) str_length(str);
+ l = 0;
+ while (l < len) {
+ unsigned m = l; /* start of current line */
+ while ((l < len) && (s[l] != nl))
+ l++;
+ sz = (int) (l - m + 7) / 4;
+ if (sz == 1)
+ sz = 2;
+ r = new_node(pseudo_line_node, sz);
+ i = r;
+ while (--sz > 1) {
+ w.b0 = s[m++];
+ w.b1 = s[m++];
+ w.b2 = s[m++];
+ w.b3 = s[m++];
+ varmem[++i].qqqq = w;
+ }
+ w.b0 = (quarterword) (l > m ? s[m++] : ' ');
+ w.b1 = (quarterword) (l > m ? s[m++] : ' ');
+ w.b2 = (quarterword) (l > m ? s[m++] : ' ');
+ w.b3 = (quarterword) (l > m ? s[m] : ' ');
+ varmem[++i].qqqq = w;
+ if (pseudo_lines(h) == null) {
+ pseudo_lines(h) = r;
+ q = r;
+ } else {
+ couple_nodes(q, r);
+ }
+ q = vlink(q);
+ if (s[l] == nl)
+ l++;
+ }
+ return h;
+}
+
+
+@ The |pseudo_start| procedure initiates reading from a pseudo file.
+
+@c
+void pseudo_from_string(void)
+{
+ str_number s; /* string to be converted into a pseudo file */
+ halfword p; /* for list construction */
+ s = make_string();
+ /* Convert string |s| into a new pseudo file */
+ p = string_to_pseudo(s, int_par(new_line_char_code));
+ vlink(p) = pseudo_files;
+ pseudo_files = p;
+ flush_str(s);
+ /* Initiate input from new pseudo file */
+ begin_file_reading(); /* set up |cur_file| and new level of input */
+ line = 0;
+ ilimit = istart;
+ iloc = ilimit + 1; /* force line read */
+ if (int_par(tracing_scan_tokens_code) > 0) {
+ if (term_offset > max_print_line - 3)
+ print_ln();
+ else if ((term_offset > 0) || (file_offset > 0))
+ print_char(' ');
+ iname = 20;
+ tprint("( ");
+ incr(open_parens);
+ update_terminal();
+ } else {
+ iname = 18;
+ }
+ /* Prepare pseudo file {\sl Sync\TeX} information */
+ synctex_tag = 0;
+}
+
+void pseudo_start(void)
+{
+ int old_setting; /* holds |selector| setting */
+ scan_general_text();
+ old_setting = selector;
+ selector = new_string;
+ token_show(temp_token_head);
+ selector = old_setting;
+ flush_list(token_link(temp_token_head));
+ str_room(1);
+ pseudo_from_string();
+}
+
+@ @c
+void lua_string_start(void)
+{
+ begin_file_reading(); /* set up |cur_file| and new level of input */
+ line = 0;
+ ilimit = istart;
+ iloc = ilimit + 1; /* force line read */
+ iname = 21;
+ luacstring_start(iindex);
+}
+
+@ Here we read a line from the current pseudo file into |buffer|.
+
+@c
+boolean pseudo_input(void)
+{ /* inputs the next line or returns |false| */
+ halfword p; /* current line from pseudo file */
+ int sz; /* size of node |p| */
+ four_quarters w; /* four ASCII codes */
+ halfword r; /* loop index */
+ last = first; /* cf.\ Matthew 19\thinspace:\thinspace30 */
+ p = pseudo_lines(pseudo_files);
+ if (p == null) {
+ return false;
+ } else {
+ pseudo_lines(pseudo_files) = vlink(p);
+ sz = subtype(p);
+ if (4 * sz - 3 >= buf_size - last)
+ check_buffer_overflow(last + 4 * sz);
+ last = first;
+ for (r = p + 1; r <= p + sz - 1; r++) {
+ w = varmem[r].qqqq;
+ buffer[last] = (packed_ASCII_code) w.b0;
+ buffer[last + 1] = (packed_ASCII_code) w.b1;
+ buffer[last + 2] = (packed_ASCII_code) w.b2;
+ buffer[last + 3] = (packed_ASCII_code) w.b3;
+ last += 4;
+ }
+ if (last >= max_buf_stack)
+ max_buf_stack = last + 1;
+ while ((last > first) && (buffer[last - 1] == ' '))
+ decr(last);
+ flush_node(p);
+ }
+ return true;
+}
+
+@ When we are done with a pseudo file we `close' it
+
+@c
+void pseudo_close(void)
+{ /* close the top level pseudo file */
+ halfword p;
+ p = vlink(pseudo_files);
+ flush_node(pseudo_files);
+ pseudo_files = p;
+}