1% inputstack.w
2%
3% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
4%
5% This file is part of LuaTeX.
6%
7% LuaTeX is free software; you can redistribute it and/or modify it under
8% the terms of the GNU General Public License as published by the Free
9% Software Foundation; either version 2 of the License, or (at your
10% option) any later version.
11%
12% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14% FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15% License for more details.
16%
17% You should have received a copy of the GNU General Public License along
18% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
19
20@ @c
21
22
23#include "ptexlib.h"
24
25@ @c
26#define end_line_char int_par(end_line_char_code)
27#define error_context_lines int_par(error_context_lines_code)
28
29
30in_state_record *input_stack = NULL;
31int input_ptr = 0;              /* first unused location of |input_stack| */
32int max_in_stack = 0;           /* largest value of |input_ptr| when pushing */
33in_state_record cur_input;      /* the ``top'' input state */
34
35
36int in_open = 0;                /* the number of lines in the buffer, less one */
37int open_parens = 0;            /* the number of open text files */
38alpha_file *input_file = NULL;
39int line = 0;                   /* current line number in the current source file */
40int *line_stack = NULL;
41str_number *source_filename_stack = NULL;
42char **full_source_filename_stack = NULL;
43
44
45int scanner_status = 0;         /* can a subfile end now? */
46pointer warning_index = null;   /* identifier relevant to non-|normal| scanner status */
47pointer def_ref = null;         /* reference count of token list being defined */
48
49
50
51@ Here is a procedure that uses |scanner_status| to print a warning message
52when a subfile has ended, and at certain other crucial times:
53
54@c
55void runaway(void)
56{
57    pointer p = null;           /* head of runaway list */
58    if (scanner_status > skipping) {
59        switch (scanner_status) {
60        case defining:
61            tprint_nl("Runaway definition");
62            p = def_ref;
63            break;
64        case matching:
65            tprint_nl("Runaway argument");
66            p = temp_token_head;
67            break;
68        case aligning:
69            tprint_nl("Runaway preamble");
70            p = hold_token_head;
71            break;
72        case absorbing:
73            tprint_nl("Runaway text");
74            p = def_ref;
75            break;
76        default:               /* there are no other cases */
77            break;
78        }
79        print_char('?');
80        print_ln();
81        show_token_list(token_link(p), null, error_line - 10);
82    }
83}
84
85
86@ The |param_stack| is an auxiliary array used to hold pointers to the token
87lists for parameters at the current level and subsidiary levels of input.
88This stack is maintained with convention (2), and it grows at a different
89rate from the others.
90
91@c
92pointer *param_stack = NULL;    /* token list pointers for parameters */
93int param_ptr = 0;              /* first unused entry in |param_stack| */
94int max_param_stack = 0;        /* largest value of |param_ptr|, will be |<=param_size+9| */
95
96@ The input routines must also interact with the processing of
97\.{\\halign} and \.{\\valign}, since the appearance of tab marks and
98\.{\\cr} in certain places is supposed to trigger the beginning of special
99$v_j$ template text in the scanner. This magic is accomplished by an
100|align_state| variable that is increased by~1 when a `\.{\char'173}' is
101scanned and decreased by~1 when a `\.{\char'175}' is scanned. The |align_state|
102is nonzero during the $u_j$ template, after which it is set to zero; the
103$v_j$ template begins when a tab mark or \.{\\cr} occurs at a time that
104|align_state=0|.
105
106@c
107int align_state = 0;            /* group level with respect to current alignment */
108
109
110@ Thus, the ``current input state'' can be very complicated indeed; there
111can be many levels and each level can arise in a variety of ways. The
112|show_context| procedure, which is used by \TeX's error-reporting routine to
113print out the current input state on all levels down to the most recent
114line of characters from an input file, illustrates most of these conventions.
115The global variable |base_ptr| contains the lowest level that was
116displayed by this procedure.
117
118@c
119int base_ptr = 0;               /* shallowest level shown by |show_context| */
120
121
122@ The status at each level is indicated by printing two lines, where the first
123line indicates what was read so far and the second line shows what remains
124to be read. The context is cropped, if necessary, so that the first line
125contains at most |half_error_line| characters, and the second contains
126at most |error_line|. Non-current input levels whose |token_type| is
127`|backed_up|' are shown only if they have not been fully read.
128
129@c
130static void print_token_list_type(int t)
131{
132    switch (t) {
133    case parameter:
134        tprint_nl("<argument> ");
135        break;
136    case u_template:
137    case v_template:
138        tprint_nl("<template> ");
139        break;
140    case backed_up:
141        if (iloc == null)
142            tprint_nl("<recently read> ");
143        else
144            tprint_nl("<to be read again> ");
145        break;
146    case inserted:
147        tprint_nl("<inserted text> ");
148        break;
149    case macro:
150        print_ln();
151        print_cs(iname);
152        break;
153    case output_text:
154        tprint_nl("<output> ");
155        break;
156    case every_par_text:
157        tprint_nl("<everypar> ");
158        break;
159    case every_math_text:
160        tprint_nl("<everymath> ");
161        break;
162    case every_display_text:
163        tprint_nl("<everydisplay> ");
164        break;
165    case every_hbox_text:
166        tprint_nl("<everyhbox> ");
167        break;
168    case every_vbox_text:
169        tprint_nl("<everyvbox> ");
170        break;
171    case every_job_text:
172        tprint_nl("<everyjob> ");
173        break;
174    case every_cr_text:
175        tprint_nl("<everycr> ");
176        break;
177    case mark_text:
178        tprint_nl("<mark> ");
179        break;
180    case every_eof_text:
181        tprint_nl("<everyeof> ");
182        break;
183    case write_text:
184        tprint_nl("<write> ");
185        break;
186    default:
187        tprint_nl("?");
188        break;                  /* this should never happen */
189    }
190}
191
192
193@ Here it is necessary to explain a little trick. We don't want to store a long
194string that corresponds to a token list, because that string might take up
195lots of memory; and we are printing during a time when an error message is
196being given, so we dare not do anything that might overflow one of \TeX's
197tables. So `pseudoprinting' is the answer: We enter a mode of printing
198that stores characters into a buffer of length |error_line|, where character
199$k+1$ is placed into \hbox{|trick_buf[k mod error_line]|} if
200|k<trick_count|, otherwise character |k| is dropped. Initially we set
201|tally:=0| and |trick_count:=1000000|; then when we reach the
202point where transition from line 1 to line 2 should occur, we
203set |first_count:=tally| and |trick_count:=@tmax@>(error_line,
204tally+1+error_line-half_error_line)|. At the end of the
205pseudoprinting, the values of |first_count|, |tally|, and
206|trick_count| give us all the information we need to print the two lines,
207and all of the necessary text is in |trick_buf|.
208
209Namely, let |l| be the length of the descriptive information that appears
210on the first line. The length of the context information gathered for that
211line is |k=first_count|, and the length of the context information
212gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|,
213where |h=half_error_line|, we print |trick_buf[0..k-1]| after the
214descriptive information on line~1, and set |n:=l+k|; here |n| is the
215length of line~1. If $l+k>h$, some cropping is necessary, so we set |n:=h|
216and print `\.{...}' followed by
217$$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$
218where subscripts of |trick_buf| are circular modulo |error_line|. The
219second line consists of |n|~spaces followed by |trick_buf[k..(k+m-1)]|,
220unless |n+m>error_line|; in the latter case, further cropping is done.
221This is easier to program than to explain.
222
223
224
225@ The following code sets up the print routines so that they will gather
226the desired information.
227
228@c
229void set_trick_count(void)
230{
231    first_count = tally;
232    trick_count = tally + 1 + error_line - half_error_line;
233    if (trick_count < error_line)
234        trick_count = error_line;
235}
236
237#define begin_pseudoprint() do {		\
238    l=tally; tally=0; selector=pseudo;		\
239    trick_count=1000000;			\
240  } while (0)
241
242#define PSEUDO_PRINT_THE_LINE()	do {					\
243    begin_pseudoprint();						\
244    if (buffer[ilimit]==end_line_char) j=ilimit;			\
245    else j=ilimit+1; /* determine the effective end of the line */	\
246    if (j>0) {								\
247      for (i=istart;i<=j-1;i++) {					\
248	if (i==iloc) set_trick_count();					\
249	print_char(buffer[i]);						\
250      }									\
251    }									\
252  } while (0)
253
254@ @c
255void show_context(void)
256{                               /* prints where the scanner is */
257    int old_setting;            /* saved |selector| setting */
258    int nn;                     /* number of contexts shown so far, less one */
259    boolean bottom_line;        /* have we reached the final context to be shown? */
260    int i;                      /* index into |buffer| */
261    int j;                      /* end of current line in |buffer| */
262    int l;                      /* length of descriptive information on line 1 */
263    int m;                      /* context information gathered for line 2 */
264    int n;                      /* length of line 1 */
265    int p;                      /* starting or ending place in |trick_buf| */
266    int q;                      /* temporary index */
267
268    base_ptr = input_ptr;
269    input_stack[base_ptr] = cur_input;
270    /* store current state */
271    nn = -1;
272    bottom_line = false;
273    while (true) {
274        cur_input = input_stack[base_ptr];      /* enter into the context */
275        if (istate != token_list) {
276            if ((iname > 21) || (base_ptr == 0))
277                bottom_line = true;
278        }
279        if ((base_ptr == input_ptr) || bottom_line
280            || (nn < error_context_lines)) {
281            /* Display the current context */
282            if ((base_ptr == input_ptr) || (istate != token_list) ||
283                (token_type != backed_up) || (iloc != null)) {
284                /* we omit backed-up token lists that have already been read */
285                tally = 0;      /* get ready to count characters */
286                old_setting = selector;
287                if (istate != token_list) {
288                    /* Print location of current line */
289                    /*
290                       This routine should be changed, if necessary, to give the best possible
291                       indication of where the current line resides in the input file.
292                       For example, on some systems it is best to print both a page and line number.
293                     */
294                    if (iname <= 17) {
295                        if (terminal_input) {
296                            if (base_ptr == 0)
297                                tprint_nl("<*>");
298                            else
299                                tprint_nl("<insert> ");
300                        } else {
301                            tprint_nl("<read ");
302                            if (iname == 17)
303                                print_char('*');
304                            else
305                                print_int(iname - 1);
306                            print_char('>');
307                        };
308                    } else {
309                        tprint_nl("l.");
310                        if (iindex == in_open) {
311                            print_int(line);
312                        } else {     /* input from a pseudo file */
313                            print_int(line_stack[iindex + 1]);
314                        }
315                    }
316                    print_char(' ');
317                    PSEUDO_PRINT_THE_LINE();
318                } else {
319                    print_token_list_type(token_type);
320
321                    begin_pseudoprint();
322                    if (token_type < macro)
323                        show_token_list(istart, iloc, 100000);
324                    else
325                        show_token_list(token_link(istart), iloc, 100000);      /* avoid reference count */
326                }
327                selector = old_setting; /* stop pseudoprinting */
328                /* Print two lines using the tricky pseudoprinted information */
329                if (trick_count == 1000000)
330                    set_trick_count();
331                /* |set_trick_count| must be performed */
332                if (tally < trick_count)
333                    m = tally - first_count;
334                else
335                    m = trick_count - first_count;      /* context on line 2 */
336                if (l + first_count <= half_error_line) {
337                    p = 0;
338                    n = l + first_count;
339                } else {
340                    tprint("...");
341                    p = l + first_count - half_error_line + 3;
342                    n = half_error_line;
343                }
344                for (q = p; q <= first_count - 1; q++)
345                    print_char(trick_buf[(q % error_line)]);
346                print_ln();
347                for (q = 1; q <= n; q++)
348                    print_char(' ');    /* print |n| spaces to begin line~2 */
349                if (m + n <= error_line)
350                    p = first_count + m;
351                else
352                    p = first_count + (error_line - n - 3);
353                for (q = first_count; q <= p - 1; q++)
354                    print_char(trick_buf[(q % error_line)]);
355                if (m + n > error_line)
356                    tprint("...");
357
358                incr(nn);
359            }
360        } else if (nn == error_context_lines) {
361            tprint_nl("...");
362            incr(nn);           /* omitted if |error_context_lines<0| */
363        }
364        if (bottom_line)
365            break;
366        decr(base_ptr);
367    }
368    cur_input = input_stack[input_ptr]; /* restore original state */
369}
370
371
372@ The following subroutines change the input status in commonly needed ways.
373
374First comes |push_input|, which stores the current state and creates a
375new level (having, initially, the same properties as the old).
376
377@c
378/* enter a new input level, save the old */
379
380void push_input(void)
381{
382    if (input_ptr > max_in_stack) {
383        max_in_stack = input_ptr;
384        if (input_ptr == stack_size)
385            overflow("input stack size", (unsigned) stack_size);
386    }
387    input_stack[input_ptr] = cur_input; /* stack the record */
388    nofilter = false;
389    incr(input_ptr);
390}
391
392@
393Here is a procedure that starts a new level of token-list input, given
394a token list |p| and its type |t|. If |t=macro|, the calling routine should
395set |name| and |loc|.
396
397@c
398void begin_token_list(halfword p, quarterword t)
399{
400    push_input();
401    istate = token_list;
402    istart = p;
403    token_type = (unsigned char) t;
404    if (t >= macro) {           /* the token list starts with a reference count */
405        add_token_ref(p);
406        if (t == macro) {
407            param_start = param_ptr;
408        } else {
409            iloc = token_link(p);
410            if (int_par(tracing_macros_code) > 1) {
411                begin_diagnostic();
412                tprint_nl("");
413                if (t == mark_text)
414                    tprint_esc("mark");
415                else if (t == write_text)
416                    tprint_esc("write");
417                else
418                    print_cmd_chr(assign_toks_cmd,
419                                  t - output_text + output_routine_loc);
420                tprint("->");
421                token_show(p);
422                end_diagnostic(false);
423            }
424        }
425    } else {
426        iloc = p;
427    }
428}
429
430
431@ When a token list has been fully scanned, the following computations
432should be done as we leave that level of input. The |token_type| tends
433to be equal to either |backed_up| or |inserted| about 2/3 of the time.
434@^inner loop@>
435
436@c
437void end_token_list(void)
438{                               /* leave a token-list input level */
439    if (token_type >= backed_up) {      /* token list to be deleted */
440        if (token_type <= inserted) {
441            flush_list(istart);
442        } else {
443            delete_token_ref(istart);   /* update reference count */
444            if (token_type == macro) {  /* parameters must be flushed */
445                while (param_ptr > param_start) {
446                    decr(param_ptr);
447                    flush_list(param_stack[param_ptr]);
448                }
449            }
450        }
451    } else if (token_type == u_template) {
452        if (align_state > 500000)
453            align_state = 0;
454        else
455            fatal_error("(interwoven alignment preambles are not allowed)");
456    }
457    pop_input();
458    check_interrupt();
459}
460
461
462@ Sometimes \TeX\ has read too far and wants to ``unscan'' what it has
463seen. The |back_input| procedure takes care of this by putting the token
464just scanned back into the input stream, ready to be read again. This
465procedure can be used only if |cur_tok| represents the token to be
466replaced. Some applications of \TeX\ use this procedure a lot,
467so it has been slightly optimized for speed.
468@^inner loop@>
469
470@c
471void back_input(void)
472{                               /* undoes one token of input */
473    halfword p;                 /* a token list of length one */
474    while ((istate == token_list) && (iloc == null)
475           && (token_type != v_template))
476        end_token_list();       /* conserve stack space */
477    p = get_avail();
478    set_token_info(p, cur_tok);
479    if (cur_tok < right_brace_limit) {
480        if (cur_tok < left_brace_limit)
481            decr(align_state);
482        else
483            incr(align_state);
484    }
485    push_input();
486    istate = token_list;
487    istart = p;
488    token_type = backed_up;
489    iloc = p;                   /* that was |back_list(p)|, without procedure overhead */
490}
491
492@ Insert token |p| into \TeX's input
493@c
494int reinsert_token(boolean a, halfword pp)
495{
496    halfword t;
497    t = cur_tok;
498    cur_tok = pp;
499    if (a) {
500        halfword p;
501        p = get_avail();
502        set_token_info(p, cur_tok);
503        set_token_link(p, iloc);
504        iloc = p;
505        istart = p;
506        if (cur_tok < right_brace_limit) {
507            if (cur_tok < left_brace_limit)
508                decr(align_state);
509            else
510                incr(align_state);
511        }
512    } else {
513        back_input();
514        a = true;               /* etex is always on */
515    }
516    cur_tok = t;
517    return a;
518}
519
520
521@ The |begin_file_reading| procedure starts a new level of input for lines
522of characters to be read from a file, or as an insertion from the
523terminal. It does not take care of opening the file, nor does it set |loc|
524or |limit| or |line|.
525@^system dependencies@>
526
527@c
528void begin_file_reading(void)
529{
530    if (in_open == max_in_open)
531        overflow("text input levels", (unsigned) max_in_open);
532    if (first == buf_size)
533        check_buffer_overflow(first);
534    incr(in_open);
535    push_input();
536    iindex = (unsigned char) in_open;
537    source_filename_stack[iindex] = 0;
538    full_source_filename_stack[iindex] = NULL;
539    eof_seen[iindex] = false;
540    grp_stack[iindex] = cur_boundary;
541    if_stack[iindex] = cond_ptr;
542    line_stack[iindex] = line;
543    istart = first;
544    istate = mid_line;
545    iname = 0;                  /* |terminal_input| is now |true| */
546    line_catcode_table = DEFAULT_CAT_TABLE;
547    line_partial = false;
548    /* Prepare terminal input {\sl Sync\TeX} information */
549    synctex_tag = 0;
550}
551
552
553@ Conversely, the variables must be downdated when such a level of input
554is finished:
555
556@c
557void end_file_reading(void)
558{
559    first = istart;
560    line = line_stack[iindex];
561    if ((iname >= 18) && (iname <= 20))
562        pseudo_close();
563    else if (iname == 21)
564        luacstring_close(iindex);
565    else if (iname > 17)
566        lua_a_close_in(cur_file, 0);    /* forget it */
567    pop_input();
568    decr(in_open);
569}
570
571
572@ In order to keep the stack from overflowing during a long sequence of
573inserted `\.{\\show}' commands, the following routine removes completed
574error-inserted lines from memory.
575
576@c
577void clear_for_error_prompt(void)
578{
579    while ((istate != token_list) && terminal_input
580           && (input_ptr > 0) && (iloc > ilimit))
581        end_file_reading();
582    print_ln();
583    clear_terminal();
584}
585
586@ To get \TeX's whole input mechanism going, we perform the following
587   actions.
588
589@c
590void initialize_inputstack(void)
591{
592    input_ptr = 0;
593    max_in_stack = 0;
594    source_filename_stack[0] = 0;
595
596    full_source_filename_stack[0] = NULL;
597    in_open = 0;
598    open_parens = 0;
599    max_buf_stack = 0;
600
601    grp_stack[0] = 0;
602    if_stack[0] = null;
603    param_ptr = 0;
604    max_param_stack = 0;
605    first = buf_size;
606    do {
607        buffer[first] = 0;
608        decr(first);
609    } while (first != 0);
610    scanner_status = normal;
611    warning_index = null;
612    first = 1;
613    istate = new_line;
614    istart = 1;
615    iindex = 0;
616    line = 0;
617    iname = 0;
618    nofilter = false;
619    force_eof = false;
620    luacstrings = 0;
621    line_catcode_table = DEFAULT_CAT_TABLE;
622    line_partial = false;
623    align_state = 1000000;
624    if (!init_terminal())
625        exit(EXIT_FAILURE);     /* |goto final_end|; */
626    ilimit = last;
627    first = last + 1;           /* |init_terminal| has set |loc| and |last| */
628}
629
630
631
632
633@ The global variable |pseudo_files| is used to maintain a stack of
634pseudo files.  The |pseudo_lines| field of each pseudo file points to
635a linked list of variable size nodes representing lines not yet
636processed: the |subtype| field contains the size of this node,
637all the following words contain ASCII codes.
638
639@c
640halfword pseudo_files;          /* stack of pseudo files */
641
642static halfword string_to_pseudo(str_number str, int nl)
643{
644    halfword i, r, q = null;
645    unsigned l, len;
646    four_quarters w;
647    int sz;
648    halfword h = new_node(pseudo_file_node, 0);
649    unsigned char *s = str_string(str);
650    len = (unsigned) str_length(str);
651    l = 0;
652    while (l < len) {
653        unsigned m = l;         /* start of current line */
654        while ((l < len) && (s[l] != nl))
655            l++;
656        sz = (int) (l - m + 7) / 4;
657        if (sz == 1)
658            sz = 2;
659        r = new_node(pseudo_line_node, sz);
660        i = r;
661        while (--sz > 1) {
662            w.b0 = s[m++];
663            w.b1 = s[m++];
664            w.b2 = s[m++];
665            w.b3 = s[m++];
666            varmem[++i].qqqq = w;
667        }
668        w.b0 = (quarterword) (l > m ? s[m++] : ' ');
669        w.b1 = (quarterword) (l > m ? s[m++] : ' ');
670        w.b2 = (quarterword) (l > m ? s[m++] : ' ');
671        w.b3 = (quarterword) (l > m ? s[m] : ' ');
672        varmem[++i].qqqq = w;
673        if (pseudo_lines(h) == null) {
674            pseudo_lines(h) = r;
675            q = r;
676        } else {
677            couple_nodes(q, r);
678        }
679        q = vlink(q);
680        if (s[l] == nl)
681            l++;
682    }
683    return h;
684}
685
686
687@ The |pseudo_start| procedure initiates reading from a pseudo file.
688
689@c
690void pseudo_from_string(void)
691{
692    str_number s;               /* string to be converted into a pseudo file */
693    halfword p;                 /* for list construction */
694    s = make_string();
695    /* Convert string |s| into a new pseudo file */
696    p = string_to_pseudo(s, int_par(new_line_char_code));
697    vlink(p) = pseudo_files;
698    pseudo_files = p;
699    flush_str(s);
700    /* Initiate input from new pseudo file */
701    begin_file_reading();       /* set up |cur_file| and new level of input */
702    line = 0;
703    ilimit = istart;
704    iloc = ilimit + 1;          /* force line read */
705    if (int_par(tracing_scan_tokens_code) > 0) {
706        if (term_offset > max_print_line - 3)
707            print_ln();
708        else if ((term_offset > 0) || (file_offset > 0))
709            print_char(' ');
710        iname = 20;
711        tprint("( ");
712        incr(open_parens);
713        update_terminal();
714    } else {
715        iname = 18;
716    }
717    /* Prepare pseudo file {\sl Sync\TeX} information */
718    synctex_tag = 0;
719}
720
721void pseudo_start(void)
722{
723    int old_setting;            /* holds |selector| setting */
724    scan_general_text();
725    old_setting = selector;
726    selector = new_string;
727    token_show(temp_token_head);
728    selector = old_setting;
729    flush_list(token_link(temp_token_head));
730    str_room(1);
731    pseudo_from_string();
732}
733
734@ @c
735void lua_string_start(void)
736{
737    begin_file_reading();       /* set up |cur_file| and new level of input */
738    line = 0;
739    ilimit = istart;
740    iloc = ilimit + 1;          /* force line read */
741    iname = 21;
742    luacstring_start(iindex);
743}
744
745@ Here we read a line from the current pseudo file into |buffer|.
746
747@c
748boolean pseudo_input(void)
749{                               /* inputs the next line or returns |false| */
750    halfword p;                 /* current line from pseudo file */
751    int sz;                     /* size of node |p| */
752    four_quarters w;            /* four ASCII codes */
753    halfword r;                 /* loop index */
754    last = first;               /* cf.\ Matthew 19\thinspace:\thinspace30 */
755    p = pseudo_lines(pseudo_files);
756    if (p == null) {
757        return false;
758    } else {
759        pseudo_lines(pseudo_files) = vlink(p);
760        sz = subtype(p);
761        if (4 * sz - 3 >= buf_size - last)
762            check_buffer_overflow(last + 4 * sz);
763        last = first;
764        for (r = p + 1; r <= p + sz - 1; r++) {
765            w = varmem[r].qqqq;
766            buffer[last] = (packed_ASCII_code) w.b0;
767            buffer[last + 1] = (packed_ASCII_code) w.b1;
768            buffer[last + 2] = (packed_ASCII_code) w.b2;
769            buffer[last + 3] = (packed_ASCII_code) w.b3;
770            last += 4;
771        }
772        if (last >= max_buf_stack)
773            max_buf_stack = last + 1;
774        while ((last > first) && (buffer[last - 1] == ' '))
775            decr(last);
776        flush_node(p);
777    }
778    return true;
779}
780
781@ When we are done with a pseudo file we `close' it.
782
783@c
784void pseudo_close(void)
785{                               /* close the top level pseudo file */
786    halfword p;
787    p = vlink(pseudo_files);
788    flush_node(pseudo_files);
789    pseudo_files = p;
790}
791