2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2004
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
26 #include "dictionary.h"
28 #include "stringclass.h"
39 int exit_started = 0; // the exit process has started
40 int done_end_macro = 0; // the end macro (if any) has finished
41 int seen_last_page_ejector = 0; // seen the LAST_PAGE_EJECTOR cookie
42 int last_page_number = 0; // if > 0, the number of the last page
44 static int began_page_in_end_macro = 0; // a new page was begun during the end macro
46 static int last_post_line_extra_space = 0; // needed for \n(.a
47 static int nl_reg_contents = -1;
48 static int dl_reg_contents = 0;
49 static int dn_reg_contents = 0;
50 static int vertical_position_traps_flag = 1;
51 static vunits truncated_space;
52 static vunits needed_space;
54 diversion::diversion(symbol s)
55 : prev(0), nm(s), vertical_position(V0), high_water_mark(V0),
56 any_chars_added(0), no_space_mode(0), needs_push(0), saved_seen_break(0),
57 saved_seen_space(0), saved_seen_eol(0), saved_suppress_next_eol(0),
62 struct vertical_size {
63 vunits pre_extra, post_extra, pre, post;
64 vertical_size(vunits vs, vunits post_vs);
67 vertical_size::vertical_size(vunits vs, vunits post_vs)
68 : pre_extra(V0), post_extra(V0), pre(vs), post(post_vs)
72 void node::set_vertical_size(vertical_size *)
76 void extra_size_node::set_vertical_size(vertical_size *v)
79 if (-n > v->pre_extra)
82 else if (n > v->post_extra)
86 void vertical_size_node::set_vertical_size(vertical_size *v)
94 top_level_diversion *topdiv;
98 void do_divert(int append, int boxing)
101 symbol nm = get_name();
104 curenv->seen_break = curdiv->saved_seen_break;
105 curenv->seen_space = curdiv->saved_seen_space;
106 curenv->seen_eol = curdiv->saved_seen_eol;
107 curenv->suppress_next_eol = curdiv->saved_suppress_next_eol;
109 curenv->line = curdiv->saved_line;
110 curenv->width_total = curdiv->saved_width_total;
111 curenv->space_total = curdiv->saved_space_total;
112 curenv->saved_indent = curdiv->saved_saved_indent;
113 curenv->target_text_length = curdiv->saved_target_text_length;
114 curenv->prev_line_interrupted = curdiv->saved_prev_line_interrupted;
116 diversion *temp = curdiv;
117 curdiv = curdiv->prev;
121 warning(WARN_DI, "diversion stack underflow");
124 macro_diversion *md = new macro_diversion(nm, append);
127 curdiv->saved_seen_break = curenv->seen_break;
128 curdiv->saved_seen_space = curenv->seen_space;
129 curdiv->saved_seen_eol = curenv->seen_eol;
130 curdiv->saved_suppress_next_eol = curenv->suppress_next_eol;
131 curenv->seen_break = 0;
132 curenv->seen_space = 0;
133 curenv->seen_eol = 0;
135 curdiv->saved_line = curenv->line;
136 curdiv->saved_width_total = curenv->width_total;
137 curdiv->saved_space_total = curenv->space_total;
138 curdiv->saved_saved_indent = curenv->saved_indent;
139 curdiv->saved_target_text_length = curenv->target_text_length;
140 curdiv->saved_prev_line_interrupted = curenv->prev_line_interrupted;
142 curenv->start_line();
168 void diversion::need(vunits n)
170 vunits d = distance_to_next_trap();
172 truncated_space = -d;
178 macro_diversion::macro_diversion(symbol s, int append)
179 : diversion(s), max_width(H0)
183 /* We don't allow recursive appends eg:
189 This causes an infinite loop in troff anyway.
190 This is because the user could do
194 in the diversion, and this would mess things up royally,
195 since there would be two things appending to the same
197 To make it work, we would have to copy the _contents_
198 of the macro into which we were diverting; this doesn't
199 strike me as worthwhile.
207 will work and will make `a' contain two copies of what it contained
208 before; in troff, `a' would contain nothing. */
210 = (request_or_macro *)request_dictionary.remove(s);
211 if (!rm || (mac = rm->to_macro()) == 0)
217 // We can now catch the situation described above by comparing
218 // the length of the charlist in the macro_header with the length
219 // stored in the macro. When we detect this, we copy the contents.
223 = (request_or_macro *)request_dictionary.lookup(s);
225 macro *m = rm->to_macro();
232 macro_diversion::~macro_diversion()
234 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
235 macro *m = rm ? rm->to_macro() : 0;
241 request_dictionary.define(nm, mac);
243 dl_reg_contents = max_width.to_units();
244 dn_reg_contents = vertical_position.to_units();
247 vunits macro_diversion::distance_to_next_trap()
249 if (!diversion_trap.is_null() && diversion_trap_pos > vertical_position)
250 return diversion_trap_pos - vertical_position;
252 // Substract vresolution so that vunits::vunits does not overflow.
253 return vunits(INT_MAX - vresolution);
256 void macro_diversion::transparent_output(unsigned char c)
261 void macro_diversion::transparent_output(node *n)
266 void macro_diversion::output(node *nd, int retain_size,
267 vunits vs, vunits post_vs, hunits width)
270 vertical_size v(vs, post_vs);
272 nd->set_vertical_size(&v);
275 if (temp->interpret(mac))
279 temp->freeze_space();
284 last_post_line_extra_space = v.post_extra.to_units();
289 if (width > max_width)
291 vunits x = v.pre + v.pre_extra + v.post + v.post_extra;
292 if (vertical_position_traps_flag
293 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
294 && diversion_trap_pos <= vertical_position + x) {
295 vunits trunc = vertical_position + x - diversion_trap_pos;
300 truncated_space = trunc;
301 spring_trap(diversion_trap);
303 mac->append(new vertical_size_node(-v.pre));
304 mac->append(new vertical_size_node(v.post));
306 vertical_position += x;
307 if (vertical_position - v.post > high_water_mark)
308 high_water_mark = vertical_position - v.post;
311 void macro_diversion::space(vunits n, int)
313 if (vertical_position_traps_flag
314 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
315 && diversion_trap_pos <= vertical_position + n) {
316 truncated_space = vertical_position + n - diversion_trap_pos;
317 n = diversion_trap_pos - vertical_position;
318 spring_trap(diversion_trap);
320 else if (n + vertical_position < V0)
321 n = -vertical_position;
322 mac->append(new diverted_space_node(n));
323 vertical_position += n;
326 void macro_diversion::copy_file(const char *filename)
328 mac->append(new diverted_copy_file_node(filename));
331 top_level_diversion::top_level_diversion()
332 : page_number(0), page_count(0), last_page_count(-1),
333 page_length(units_per_inch*11),
334 prev_page_offset(units_per_inch), page_offset(units_per_inch),
335 page_trap_list(0), have_next_page_number(0),
336 ejecting_page(0), before_first_page(1)
340 // find the next trap after pos
342 trap *top_level_diversion::find_next_trap(vunits *next_trap_pos)
345 for (trap *pt = page_trap_list; pt != 0; pt = pt->next)
346 if (!pt->nm.is_null()) {
347 if (pt->position >= V0) {
348 if (pt->position > vertical_position
349 && pt->position < page_length
350 && (next_trap == 0 || pt->position < *next_trap_pos)) {
352 *next_trap_pos = pt->position;
356 vunits pos = pt->position;
358 if (pos > 0 && pos > vertical_position && (next_trap == 0 || pos < *next_trap_pos)) {
360 *next_trap_pos = pos;
367 vunits top_level_diversion::distance_to_next_trap()
370 if (!find_next_trap(&d))
371 return page_length - vertical_position;
373 return d - vertical_position;
376 void top_level_diversion::output(node *nd, int retain_size,
377 vunits vs, vunits post_vs, hunits width)
380 vunits next_trap_pos;
381 trap *next_trap = find_next_trap(&next_trap_pos);
382 if (before_first_page && begin_page())
383 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
384 vertical_size v(vs, post_vs);
385 for (node *tem = nd; tem != 0; tem = tem->next)
386 tem->set_vertical_size(&v);
387 last_post_line_extra_space = v.post_extra.to_units();
392 vertical_position += v.pre;
393 vertical_position += v.pre_extra;
394 the_output->print_line(page_offset, vertical_position, nd,
395 v.pre + v.pre_extra, v.post_extra, width);
396 vertical_position += v.post_extra;
397 if (vertical_position > high_water_mark)
398 high_water_mark = vertical_position;
399 if (vertical_position_traps_flag && vertical_position >= page_length)
401 else if (vertical_position_traps_flag
402 && next_trap != 0 && vertical_position >= next_trap_pos) {
403 nl_reg_contents = vertical_position.to_units();
404 truncated_space = v.post;
405 spring_trap(next_trap->nm);
407 else if (v.post > V0) {
408 vertical_position += v.post;
409 if (vertical_position_traps_flag
410 && next_trap != 0 && vertical_position >= next_trap_pos) {
411 truncated_space = vertical_position - next_trap_pos;
412 vertical_position = next_trap_pos;
413 nl_reg_contents = vertical_position.to_units();
414 spring_trap(next_trap->nm);
416 else if (vertical_position_traps_flag && vertical_position >= page_length)
419 nl_reg_contents = vertical_position.to_units();
422 nl_reg_contents = vertical_position.to_units();
425 void top_level_diversion::transparent_output(unsigned char c)
427 if (before_first_page && begin_page())
428 // This can only happen with the .output request.
429 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
430 const char *s = asciify(c);
432 the_output->transparent_char(*s++);
435 void top_level_diversion::transparent_output(node * /*n*/)
437 error("can't transparently output node at top level");
440 void top_level_diversion::copy_file(const char *filename)
442 if (before_first_page && begin_page())
443 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
444 the_output->copy_file(page_offset, vertical_position, filename);
447 void top_level_diversion::space(vunits n, int forced)
455 if (before_first_page) {
459 vunits next_trap_pos;
460 trap *next_trap = find_next_trap(&next_trap_pos);
461 vunits y = vertical_position + n;
462 if (curenv->get_vertical_spacing().to_units())
463 curenv->seen_space += n.to_units()
464 / curenv->get_vertical_spacing().to_units();
465 if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) {
466 vertical_position = next_trap_pos;
467 nl_reg_contents = vertical_position.to_units();
468 truncated_space = y - vertical_position;
469 spring_trap(next_trap->nm);
472 vertical_position = V0;
473 nl_reg_contents = vertical_position.to_units();
475 else if (vertical_position_traps_flag && y >= page_length && n >= V0)
476 begin_page(y - page_length);
478 vertical_position = y;
479 nl_reg_contents = vertical_position.to_units();
483 trap::trap(symbol s, vunits n, trap *p)
484 : next(p), position(n), nm(s)
488 void top_level_diversion::add_trap(symbol nam, vunits pos)
490 trap *first_free_slot = 0;
492 for (p = &page_trap_list; *p; p = &(*p)->next) {
493 if ((*p)->nm.is_null()) {
494 if (first_free_slot == 0)
495 first_free_slot = *p;
497 else if ((*p)->position == pos) {
502 if (first_free_slot) {
503 first_free_slot->nm = nam;
504 first_free_slot->position = pos;
507 *p = new trap(nam, pos, 0);
510 void top_level_diversion::remove_trap(symbol nam)
512 for (trap *p = page_trap_list; p; p = p->next)
519 void top_level_diversion::remove_trap_at(vunits pos)
521 for (trap *p = page_trap_list; p; p = p->next)
522 if (p->position == pos) {
528 void top_level_diversion::change_trap(symbol nam, vunits pos)
530 for (trap *p = page_trap_list; p; p = p->next)
537 void top_level_diversion::print_traps()
539 for (trap *p = page_trap_list; p; p = p->next)
541 fprintf(stderr, " empty\n");
543 fprintf(stderr, "%s\t%d\n", p->nm.contents(), p->position.to_units());
547 void end_diversions()
549 while (curdiv != topdiv) {
550 error("automatically ending diversion `%1' on exit",
551 curdiv->nm.contents());
552 diversion *tem = curdiv;
553 curdiv = curdiv->prev;
558 void cleanup_and_exit(int exit_code)
561 the_output->trailer(topdiv->get_page_length());
564 FLUSH_INPUT_PIPE(STDIN_FILENO);
568 // Returns non-zero if it sprung a top-of-page trap.
569 // The optional parameter is for the .trunc register.
570 int top_level_diversion::begin_page(vunits n)
573 if (page_count == last_page_count
575 : (done_end_macro && (seen_last_page_ejector || began_page_in_end_macro)))
578 began_page_in_end_macro = 1;
580 if (last_page_number > 0 && page_number == last_page_number)
585 if (have_next_page_number) {
586 page_number = next_page_number;
587 have_next_page_number = 0;
589 else if (before_first_page == 1)
593 // spring the top of page trap if there is one
594 vunits next_trap_pos;
595 vertical_position = -vresolution;
596 trap *next_trap = find_next_trap(&next_trap_pos);
597 vertical_position = V0;
598 high_water_mark = V0;
600 // If before_first_page was 2, then the top of page transition was undone
601 // using eg .nr nl 0-1. See nl_reg::set_value.
602 if (before_first_page != 2)
603 the_output->begin_page(page_number, page_length);
604 before_first_page = 0;
605 nl_reg_contents = vertical_position.to_units();
606 if (vertical_position_traps_flag && next_trap != 0 && next_trap_pos == V0) {
608 spring_trap(next_trap->nm);
615 void continue_page_eject()
617 if (topdiv->get_ejecting()) {
618 if (curdiv != topdiv)
619 error("can't continue page ejection because of current diversion");
620 else if (!vertical_position_traps_flag)
621 error("can't continue page ejection because vertical position traps disabled");
624 topdiv->space(topdiv->get_page_length(), 1);
629 void top_level_diversion::set_next_page_number(int n)
632 have_next_page_number = 1;
635 int top_level_diversion::get_next_page_number()
637 return have_next_page_number ? next_page_number : page_number + 1;
640 void top_level_diversion::set_page_length(vunits n)
645 diversion::~diversion()
652 // The troff manual says that the default scaling indicator is v,
653 // but it is in fact m: v wouldn't make sense for a horizontally
655 if (!has_arg() || !get_hunits(&n, 'm', topdiv->page_offset))
656 n = topdiv->prev_page_offset;
657 topdiv->prev_page_offset = topdiv->page_offset;
658 topdiv->page_offset = n;
659 topdiv->modified_tag.incl(MTSM_PO);
666 if (has_arg() && get_vunits(&n, 'v', topdiv->get_page_length()))
667 topdiv->set_page_length(n);
669 topdiv->set_page_length(11*units_per_inch);
676 if (get_vunits(&n, 'v')) {
677 symbol s = get_name();
679 topdiv->remove_trap_at(n);
681 topdiv->add_trap(s, n);
689 int n = 0; /* pacify compiler */
690 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
692 while (!tok.newline() && !tok.eof())
694 if (curdiv == topdiv) {
695 if (topdiv->before_first_page) {
698 topdiv->set_next_page_number(n);
699 if (got_arg || !topdiv->no_space_mode)
700 topdiv->begin_page();
702 else if (topdiv->no_space_mode && !got_arg)
703 topdiv->begin_page();
718 This code makes groff do the same. */
721 topdiv->begin_page();
723 topdiv->set_next_page_number(n);
724 topdiv->set_ejecting();
732 topdiv->set_next_page_number(n);
733 if (!(topdiv->no_space_mode && !got_arg))
734 topdiv->set_ejecting();
742 curdiv->no_space_mode = 1;
746 void restore_spacing()
748 curdiv->no_space_mode = 0;
752 /* It is necessary to generate a break before reading the argument,
753 because otherwise arguments using | will be wrong. But if we just
754 generate a break as usual, then the line forced out may spring a trap
755 and thus push a macro onto the input stack before we have had a chance
756 to read the argument to the sp request. We resolve this dilemma by
757 setting, before generating the break, a flag which will postpone the
758 actual pushing of the macro associated with the trap sprung by the
759 outputting of the line forced out by the break till after we have read
760 the argument to the request. If the break did cause a trap to be
761 sprung, then we don't actually do the space. */
769 if (!has_arg() || !get_vunits(&n, 'v'))
770 n = curenv->get_vertical_spacing();
771 while (!tok.newline() && !tok.eof())
773 if (!unpostpone_traps() && !curdiv->no_space_mode)
776 // The line might have had line spacing that was truncated.
777 truncated_space += n;
785 if (!trap_sprung_flag && !curdiv->no_space_mode)
786 curdiv->space(curenv->get_vertical_spacing());
788 truncated_space += curenv->get_vertical_spacing();
791 /* need_space might spring a trap and so we must be careful that the
792 BEGIN_TRAP token is not skipped over. */
797 if (!has_arg() || !get_vunits(&n, 'v'))
798 n = curenv->get_vertical_spacing();
799 while (!tok.newline() && !tok.eof())
809 // the ps4html register is set if we are using -Tps
810 // to generate images for html
811 reg *r = (reg *)number_reg_dictionary.lookup("ps4html");
813 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
814 topdiv->set_next_page_number(n);
820 void save_vertical_space()
823 if (!has_arg() || !get_vunits(&x, 'v'))
824 x = curenv->get_vertical_spacing();
825 if (curdiv->distance_to_next_trap() > x)
832 void output_saved_vertical_space()
834 while (!tok.newline() && !tok.eof())
836 if (saved_space > V0)
837 curdiv->space(saved_space, 1);
844 while (!tok.newline() && !tok.eof())
853 void macro_diversion::set_diversion_trap(symbol s, vunits n)
856 diversion_trap_pos = n;
859 void macro_diversion::clear_diversion_trap()
861 diversion_trap = NULL_SYMBOL;
864 void top_level_diversion::set_diversion_trap(symbol, vunits)
866 error("can't set diversion trap when no current diversion");
869 void top_level_diversion::clear_diversion_trap()
871 error("can't set diversion trap when no current diversion");
874 void diversion_trap()
877 if (has_arg() && get_vunits(&n, 'v')) {
878 symbol s = get_name();
880 curdiv->set_diversion_trap(s, n);
882 curdiv->clear_diversion_trap();
885 curdiv->clear_diversion_trap();
891 symbol s = get_name(1);
894 if (has_arg() && get_vunits(&x, 'v'))
895 topdiv->change_trap(s, x);
897 topdiv->remove_trap(s);
904 topdiv->print_traps();
910 symbol s = get_name();
912 curdiv->marked_place = curdiv->get_vertical_position();
913 else if (curdiv == topdiv)
914 set_number_reg(s, nl_reg_contents);
916 set_number_reg(s, curdiv->get_vertical_position().to_units());
920 // This is truly bizarre. It is documented in the SQ manual.
922 void return_request()
924 vunits dist = curdiv->marked_place - curdiv->get_vertical_position();
926 if (tok.ch() == '-') {
929 if (get_vunits(&x, 'v'))
934 if (get_vunits(&x, 'v'))
935 dist = x >= V0 ? x - curdiv->get_vertical_position() : V0;
943 void vertical_position_traps()
946 if (has_arg() && get_integer(&n))
947 vertical_position_traps_flag = (n != 0);
949 vertical_position_traps_flag = 1;
953 class page_offset_reg : public reg {
955 int get_value(units *);
956 const char *get_string();
959 int page_offset_reg::get_value(units *res)
961 *res = topdiv->get_page_offset().to_units();
965 const char *page_offset_reg::get_string()
967 return i_to_a(topdiv->get_page_offset().to_units());
970 class page_length_reg : public reg {
972 int get_value(units *);
973 const char *get_string();
976 int page_length_reg::get_value(units *res)
978 *res = topdiv->get_page_length().to_units();
982 const char *page_length_reg::get_string()
984 return i_to_a(topdiv->get_page_length().to_units());
987 class vertical_position_reg : public reg {
989 int get_value(units *);
990 const char *get_string();
993 int vertical_position_reg::get_value(units *res)
995 if (curdiv == topdiv && topdiv->before_first_page)
998 *res = curdiv->get_vertical_position().to_units();
1002 const char *vertical_position_reg::get_string()
1004 if (curdiv == topdiv && topdiv->before_first_page)
1007 return i_to_a(curdiv->get_vertical_position().to_units());
1010 class high_water_mark_reg : public reg {
1012 int get_value(units *);
1013 const char *get_string();
1016 int high_water_mark_reg::get_value(units *res)
1018 *res = curdiv->get_high_water_mark().to_units();
1022 const char *high_water_mark_reg::get_string()
1024 return i_to_a(curdiv->get_high_water_mark().to_units());
1027 class distance_to_next_trap_reg : public reg {
1029 int get_value(units *);
1030 const char *get_string();
1033 int distance_to_next_trap_reg::get_value(units *res)
1035 *res = curdiv->distance_to_next_trap().to_units();
1039 const char *distance_to_next_trap_reg::get_string()
1041 return i_to_a(curdiv->distance_to_next_trap().to_units());
1044 class diversion_name_reg : public reg {
1046 const char *get_string();
1049 const char *diversion_name_reg::get_string()
1051 return curdiv->get_diversion_name();
1054 class page_number_reg : public general_reg {
1057 int get_value(units *);
1058 void set_value(units);
1061 page_number_reg::page_number_reg()
1065 void page_number_reg::set_value(units n)
1067 topdiv->set_page_number(n);
1070 int page_number_reg::get_value(units *res)
1072 *res = topdiv->get_page_number();
1076 class next_page_number_reg : public reg {
1078 const char *get_string();
1081 const char *next_page_number_reg::get_string()
1083 return i_to_a(topdiv->get_next_page_number());
1086 class page_ejecting_reg : public reg {
1088 const char *get_string();
1091 const char *page_ejecting_reg::get_string()
1093 return i_to_a(topdiv->get_ejecting());
1096 class constant_vunits_reg : public reg {
1099 constant_vunits_reg(vunits *);
1100 const char *get_string();
1103 constant_vunits_reg::constant_vunits_reg(vunits *q) : p(q)
1107 const char *constant_vunits_reg::get_string()
1109 return i_to_a(p->to_units());
1112 class nl_reg : public variable_reg {
1115 void set_value(units);
1118 nl_reg::nl_reg() : variable_reg(&nl_reg_contents)
1122 void nl_reg::set_value(units n)
1124 variable_reg::set_value(n);
1125 // Setting nl to a negative value when the vertical position in
1126 // the top-level diversion is 0 undoes the top of page transition,
1127 // so that the header macro will be called as if the top of page
1128 // transition hasn't happened. This is used by Larry Wall's
1129 // wrapman program. Setting before_first_page to 2 rather than 1,
1130 // tells top_level_diversion::begin_page not to call
1131 // output_file::begin_page again.
1132 if (n < 0 && topdiv->get_vertical_position() == V0)
1133 topdiv->before_first_page = 2;
1136 class no_space_mode_reg : public reg {
1138 int get_value(units *);
1139 const char *get_string();
1142 int no_space_mode_reg::get_value(units *val)
1144 *val = curdiv->no_space_mode;
1148 const char *no_space_mode_reg::get_string()
1150 return curdiv->no_space_mode ? "1" : "0";
1153 void init_div_requests()
1155 init_request("box", box);
1156 init_request("boxa", box_append);
1157 init_request("bp", begin_page);
1158 init_request("ch", change_trap);
1159 init_request("da", divert_append);
1160 init_request("di", divert);
1161 init_request("dt", diversion_trap);
1162 init_request("fl", flush_output);
1163 init_request("mk", mark);
1164 init_request("ne", need_space);
1165 init_request("ns", no_space);
1166 init_request("os", output_saved_vertical_space);
1167 init_request("pl", page_length);
1168 init_request("pn", page_number);
1169 init_request("po", page_offset);
1170 init_request("ptr", print_traps);
1171 init_request("rs", restore_spacing);
1172 init_request("rt", return_request);
1173 init_request("sp", space_request);
1174 init_request("sv", save_vertical_space);
1175 init_request("vpt", vertical_position_traps);
1176 init_request("wh", when_request);
1177 number_reg_dictionary.define(".a",
1178 new constant_int_reg(&last_post_line_extra_space));
1179 number_reg_dictionary.define(".d", new vertical_position_reg);
1180 number_reg_dictionary.define(".h", new high_water_mark_reg);
1181 number_reg_dictionary.define(".ne",
1182 new constant_vunits_reg(&needed_space));
1183 number_reg_dictionary.define(".ns", new no_space_mode_reg);
1184 number_reg_dictionary.define(".o", new page_offset_reg);
1185 number_reg_dictionary.define(".p", new page_length_reg);
1186 number_reg_dictionary.define(".pe", new page_ejecting_reg);
1187 number_reg_dictionary.define(".pn", new next_page_number_reg);
1188 number_reg_dictionary.define(".t", new distance_to_next_trap_reg);
1189 number_reg_dictionary.define(".trunc",
1190 new constant_vunits_reg(&truncated_space));
1191 number_reg_dictionary.define(".vpt",
1192 new constant_int_reg(&vertical_position_traps_flag));
1193 number_reg_dictionary.define(".z", new diversion_name_reg);
1194 number_reg_dictionary.define("dl", new variable_reg(&dl_reg_contents));
1195 number_reg_dictionary.define("dn", new variable_reg(&dn_reg_contents));
1196 number_reg_dictionary.define("nl", new nl_reg);
1197 number_reg_dictionary.define("%", new page_number_reg);