2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2004, 2009
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 3 of the License, or
11 (at your option) any later version.
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
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
25 #include "dictionary.h"
27 #include "stringclass.h"
38 int exit_started = 0; // the exit process has started
39 int done_end_macro = 0; // the end macro (if any) has finished
40 int seen_last_page_ejector = 0; // seen the LAST_PAGE_EJECTOR cookie
41 int last_page_number = 0; // if > 0, the number of the last page
43 static int began_page_in_end_macro = 0; // a new page was begun during the end macro
45 static int last_post_line_extra_space = 0; // needed for \n(.a
46 static int nl_reg_contents = -1;
47 static int dl_reg_contents = 0;
48 static int dn_reg_contents = 0;
49 static int vertical_position_traps_flag = 1;
50 static vunits truncated_space;
51 static vunits needed_space;
53 diversion::diversion(symbol s)
54 : prev(0), nm(s), vertical_position(V0), high_water_mark(V0),
55 any_chars_added(0), no_space_mode(0), needs_push(0), saved_seen_break(0),
56 saved_seen_space(0), saved_seen_eol(0), saved_suppress_next_eol(0),
61 struct vertical_size {
62 vunits pre_extra, post_extra, pre, post;
63 vertical_size(vunits vs, vunits post_vs);
66 vertical_size::vertical_size(vunits vs, vunits post_vs)
67 : pre_extra(V0), post_extra(V0), pre(vs), post(post_vs)
71 void node::set_vertical_size(vertical_size *)
75 void extra_size_node::set_vertical_size(vertical_size *v)
78 if (-n > v->pre_extra)
81 else if (n > v->post_extra)
85 void vertical_size_node::set_vertical_size(vertical_size *v)
93 top_level_diversion *topdiv;
97 void do_divert(int append, int boxing)
100 symbol nm = get_name();
103 curenv->seen_break = curdiv->saved_seen_break;
104 curenv->seen_space = curdiv->saved_seen_space;
105 curenv->seen_eol = curdiv->saved_seen_eol;
106 curenv->suppress_next_eol = curdiv->saved_suppress_next_eol;
108 curenv->line = curdiv->saved_line;
109 curenv->width_total = curdiv->saved_width_total;
110 curenv->space_total = curdiv->saved_space_total;
111 curenv->saved_indent = curdiv->saved_saved_indent;
112 curenv->target_text_length = curdiv->saved_target_text_length;
113 curenv->prev_line_interrupted = curdiv->saved_prev_line_interrupted;
115 diversion *temp = curdiv;
116 curdiv = curdiv->prev;
120 warning(WARN_DI, "diversion stack underflow");
123 macro_diversion *md = new macro_diversion(nm, append);
126 curdiv->saved_seen_break = curenv->seen_break;
127 curdiv->saved_seen_space = curenv->seen_space;
128 curdiv->saved_seen_eol = curenv->seen_eol;
129 curdiv->saved_suppress_next_eol = curenv->suppress_next_eol;
130 curenv->seen_break = 0;
131 curenv->seen_space = 0;
132 curenv->seen_eol = 0;
134 curdiv->saved_line = curenv->line;
135 curdiv->saved_width_total = curenv->width_total;
136 curdiv->saved_space_total = curenv->space_total;
137 curdiv->saved_saved_indent = curenv->saved_indent;
138 curdiv->saved_target_text_length = curenv->target_text_length;
139 curdiv->saved_prev_line_interrupted = curenv->prev_line_interrupted;
141 curenv->start_line();
167 void diversion::need(vunits n)
169 vunits d = distance_to_next_trap();
171 truncated_space = -d;
177 macro_diversion::macro_diversion(symbol s, int append)
178 : diversion(s), max_width(H0)
182 /* We don't allow recursive appends eg:
188 This causes an infinite loop in troff anyway.
189 This is because the user could do
193 in the diversion, and this would mess things up royally,
194 since there would be two things appending to the same
196 To make it work, we would have to copy the _contents_
197 of the macro into which we were diverting; this doesn't
198 strike me as worthwhile.
206 will work and will make `a' contain two copies of what it contained
207 before; in troff, `a' would contain nothing. */
209 = (request_or_macro *)request_dictionary.remove(s);
210 if (!rm || (mac = rm->to_macro()) == 0)
216 // We can now catch the situation described above by comparing
217 // the length of the charlist in the macro_header with the length
218 // stored in the macro. When we detect this, we copy the contents.
222 = (request_or_macro *)request_dictionary.lookup(s);
224 macro *m = rm->to_macro();
231 macro_diversion::~macro_diversion()
233 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
234 macro *m = rm ? rm->to_macro() : 0;
240 request_dictionary.define(nm, mac);
242 dl_reg_contents = max_width.to_units();
243 dn_reg_contents = vertical_position.to_units();
246 vunits macro_diversion::distance_to_next_trap()
248 if (!diversion_trap.is_null() && diversion_trap_pos > vertical_position)
249 return diversion_trap_pos - vertical_position;
251 // Substract vresolution so that vunits::vunits does not overflow.
252 return vunits(INT_MAX - vresolution);
255 void macro_diversion::transparent_output(unsigned char c)
260 void macro_diversion::transparent_output(node *n)
265 void macro_diversion::output(node *nd, int retain_size,
266 vunits vs, vunits post_vs, hunits width)
269 vertical_size v(vs, post_vs);
271 nd->set_vertical_size(&v);
274 if (temp->interpret(mac))
278 temp->freeze_space();
283 last_post_line_extra_space = v.post_extra.to_units();
288 if (width > max_width)
290 vunits x = v.pre + v.pre_extra + v.post + v.post_extra;
291 if (vertical_position_traps_flag
292 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
293 && diversion_trap_pos <= vertical_position + x) {
294 vunits trunc = vertical_position + x - diversion_trap_pos;
299 truncated_space = trunc;
300 spring_trap(diversion_trap);
302 mac->append(new vertical_size_node(-v.pre));
303 mac->append(new vertical_size_node(v.post));
305 vertical_position += x;
306 if (vertical_position - v.post > high_water_mark)
307 high_water_mark = vertical_position - v.post;
310 void macro_diversion::space(vunits n, int)
312 if (vertical_position_traps_flag
313 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
314 && diversion_trap_pos <= vertical_position + n) {
315 truncated_space = vertical_position + n - diversion_trap_pos;
316 n = diversion_trap_pos - vertical_position;
317 spring_trap(diversion_trap);
319 else if (n + vertical_position < V0)
320 n = -vertical_position;
321 mac->append(new diverted_space_node(n));
322 vertical_position += n;
325 void macro_diversion::copy_file(const char *filename)
327 mac->append(new diverted_copy_file_node(filename));
330 top_level_diversion::top_level_diversion()
331 : page_number(0), page_count(0), last_page_count(-1),
332 page_length(units_per_inch*11),
333 prev_page_offset(units_per_inch), page_offset(units_per_inch),
334 page_trap_list(0), have_next_page_number(0),
335 ejecting_page(0), before_first_page(1)
339 // find the next trap after pos
341 trap *top_level_diversion::find_next_trap(vunits *next_trap_pos)
344 for (trap *pt = page_trap_list; pt != 0; pt = pt->next)
345 if (!pt->nm.is_null()) {
346 if (pt->position >= V0) {
347 if (pt->position > vertical_position
348 && pt->position < page_length
349 && (next_trap == 0 || pt->position < *next_trap_pos)) {
351 *next_trap_pos = pt->position;
355 vunits pos = pt->position;
358 && pos > vertical_position
359 && (next_trap == 0 || pos < *next_trap_pos)) {
361 *next_trap_pos = pos;
368 vunits top_level_diversion::distance_to_next_trap()
371 if (!find_next_trap(&d))
372 return page_length - vertical_position;
374 return d - vertical_position;
377 void top_level_diversion::output(node *nd, int retain_size,
378 vunits vs, vunits post_vs, hunits width)
381 vunits next_trap_pos;
382 trap *next_trap = find_next_trap(&next_trap_pos);
383 if (before_first_page && begin_page())
384 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
385 vertical_size v(vs, post_vs);
386 for (node *tem = nd; tem != 0; tem = tem->next)
387 tem->set_vertical_size(&v);
388 last_post_line_extra_space = v.post_extra.to_units();
393 vertical_position += v.pre;
394 vertical_position += v.pre_extra;
395 the_output->print_line(page_offset, vertical_position, nd,
396 v.pre + v.pre_extra, v.post_extra, width);
397 vertical_position += v.post_extra;
398 if (vertical_position > high_water_mark)
399 high_water_mark = vertical_position;
400 if (vertical_position_traps_flag && vertical_position >= page_length)
402 else if (vertical_position_traps_flag
403 && next_trap != 0 && vertical_position >= next_trap_pos) {
404 nl_reg_contents = vertical_position.to_units();
405 truncated_space = v.post;
406 spring_trap(next_trap->nm);
408 else if (v.post > V0) {
409 vertical_position += v.post;
410 if (vertical_position_traps_flag
411 && next_trap != 0 && vertical_position >= next_trap_pos) {
412 truncated_space = vertical_position - next_trap_pos;
413 vertical_position = next_trap_pos;
414 nl_reg_contents = vertical_position.to_units();
415 spring_trap(next_trap->nm);
417 else if (vertical_position_traps_flag && vertical_position >= page_length)
420 nl_reg_contents = vertical_position.to_units();
423 nl_reg_contents = vertical_position.to_units();
426 void top_level_diversion::transparent_output(unsigned char c)
428 if (before_first_page && begin_page())
429 // This can only happen with the .output request.
430 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
431 const char *s = asciify(c);
433 the_output->transparent_char(*s++);
436 void top_level_diversion::transparent_output(node * /*n*/)
438 error("can't transparently output node at top level");
441 void top_level_diversion::copy_file(const char *filename)
443 if (before_first_page && begin_page())
444 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
445 the_output->copy_file(page_offset, vertical_position, filename);
448 void top_level_diversion::space(vunits n, int forced)
456 if (before_first_page) {
460 vunits next_trap_pos;
461 trap *next_trap = find_next_trap(&next_trap_pos);
462 vunits y = vertical_position + n;
463 if (curenv->get_vertical_spacing().to_units())
464 curenv->seen_space += n.to_units()
465 / curenv->get_vertical_spacing().to_units();
466 if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) {
467 vertical_position = next_trap_pos;
468 nl_reg_contents = vertical_position.to_units();
469 truncated_space = y - vertical_position;
470 spring_trap(next_trap->nm);
473 vertical_position = V0;
474 nl_reg_contents = vertical_position.to_units();
476 else if (vertical_position_traps_flag && y >= page_length && n >= V0)
477 begin_page(y - page_length);
479 vertical_position = y;
480 nl_reg_contents = vertical_position.to_units();
484 trap::trap(symbol s, vunits n, trap *p)
485 : next(p), position(n), nm(s)
489 void top_level_diversion::add_trap(symbol nam, vunits pos)
491 trap *first_free_slot = 0;
493 for (p = &page_trap_list; *p; p = &(*p)->next) {
494 if ((*p)->nm.is_null()) {
495 if (first_free_slot == 0)
496 first_free_slot = *p;
498 else if ((*p)->position == pos) {
503 if (first_free_slot) {
504 first_free_slot->nm = nam;
505 first_free_slot->position = pos;
508 *p = new trap(nam, pos, 0);
511 void top_level_diversion::remove_trap(symbol nam)
513 for (trap *p = page_trap_list; p; p = p->next)
520 void top_level_diversion::remove_trap_at(vunits pos)
522 for (trap *p = page_trap_list; p; p = p->next)
523 if (p->position == pos) {
529 void top_level_diversion::change_trap(symbol nam, vunits pos)
531 for (trap *p = page_trap_list; p; p = p->next)
538 void top_level_diversion::print_traps()
540 for (trap *p = page_trap_list; p; p = p->next)
542 fprintf(stderr, " empty\n");
544 fprintf(stderr, "%s\t%d\n", p->nm.contents(), p->position.to_units());
548 void end_diversions()
550 while (curdiv != topdiv) {
551 error("automatically ending diversion `%1' on exit",
552 curdiv->nm.contents());
553 diversion *tem = curdiv;
554 curdiv = curdiv->prev;
559 void cleanup_and_exit(int exit_code)
562 the_output->trailer(topdiv->get_page_length());
565 FLUSH_INPUT_PIPE(STDIN_FILENO);
569 // Returns non-zero if it sprung a top-of-page trap.
570 // The optional parameter is for the .trunc register.
571 int top_level_diversion::begin_page(vunits n)
574 if (page_count == last_page_count
576 : (done_end_macro && (seen_last_page_ejector || began_page_in_end_macro)))
579 began_page_in_end_macro = 1;
581 if (last_page_number > 0 && page_number == last_page_number)
586 if (have_next_page_number) {
587 page_number = next_page_number;
588 have_next_page_number = 0;
590 else if (before_first_page == 1)
594 // spring the top of page trap if there is one
595 vunits next_trap_pos;
596 vertical_position = -vresolution;
597 trap *next_trap = find_next_trap(&next_trap_pos);
598 vertical_position = V0;
599 high_water_mark = V0;
601 // If before_first_page was 2, then the top of page transition was undone
602 // using eg .nr nl 0-1. See nl_reg::set_value.
603 if (before_first_page != 2)
604 the_output->begin_page(page_number, page_length);
605 before_first_page = 0;
606 nl_reg_contents = vertical_position.to_units();
607 if (vertical_position_traps_flag && next_trap != 0 && next_trap_pos == V0) {
609 spring_trap(next_trap->nm);
616 void continue_page_eject()
618 if (topdiv->get_ejecting()) {
619 if (curdiv != topdiv)
620 error("can't continue page ejection because of current diversion");
621 else if (!vertical_position_traps_flag)
622 error("can't continue page ejection because vertical position traps disabled");
625 topdiv->space(topdiv->get_page_length(), 1);
630 void top_level_diversion::set_next_page_number(int n)
633 have_next_page_number = 1;
636 int top_level_diversion::get_next_page_number()
638 return have_next_page_number ? next_page_number : page_number + 1;
641 void top_level_diversion::set_page_length(vunits n)
646 diversion::~diversion()
653 // The troff manual says that the default scaling indicator is v,
654 // but it is in fact m: v wouldn't make sense for a horizontally
656 if (!has_arg() || !get_hunits(&n, 'm', topdiv->page_offset))
657 n = topdiv->prev_page_offset;
658 topdiv->prev_page_offset = topdiv->page_offset;
659 topdiv->page_offset = n;
660 topdiv->modified_tag.incl(MTSM_PO);
667 if (has_arg() && get_vunits(&n, 'v', topdiv->get_page_length()))
668 topdiv->set_page_length(n);
670 topdiv->set_page_length(11*units_per_inch);
677 if (get_vunits(&n, 'v')) {
678 symbol s = get_name();
680 topdiv->remove_trap_at(n);
682 topdiv->add_trap(s, n);
690 int n = 0; /* pacify compiler */
691 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
693 while (!tok.newline() && !tok.eof())
695 if (curdiv == topdiv) {
696 if (topdiv->before_first_page) {
699 topdiv->set_next_page_number(n);
700 if (got_arg || !topdiv->no_space_mode)
701 topdiv->begin_page();
703 else if (topdiv->no_space_mode && !got_arg)
704 topdiv->begin_page();
719 This code makes groff do the same. */
722 topdiv->begin_page();
724 topdiv->set_next_page_number(n);
725 topdiv->set_ejecting();
733 topdiv->set_next_page_number(n);
734 if (!(topdiv->no_space_mode && !got_arg))
735 topdiv->set_ejecting();
743 curdiv->no_space_mode = 1;
747 void restore_spacing()
749 curdiv->no_space_mode = 0;
753 /* It is necessary to generate a break before reading the argument,
754 because otherwise arguments using | will be wrong. But if we just
755 generate a break as usual, then the line forced out may spring a trap
756 and thus push a macro onto the input stack before we have had a chance
757 to read the argument to the sp request. We resolve this dilemma by
758 setting, before generating the break, a flag which will postpone the
759 actual pushing of the macro associated with the trap sprung by the
760 outputting of the line forced out by the break till after we have read
761 the argument to the request. If the break did cause a trap to be
762 sprung, then we don't actually do the space. */
770 if (!has_arg() || !get_vunits(&n, 'v'))
771 n = curenv->get_vertical_spacing();
772 while (!tok.newline() && !tok.eof())
774 if (!unpostpone_traps() && !curdiv->no_space_mode)
777 // The line might have had line spacing that was truncated.
778 truncated_space += n;
786 if (!trap_sprung_flag && !curdiv->no_space_mode)
787 curdiv->space(curenv->get_vertical_spacing());
789 truncated_space += curenv->get_vertical_spacing();
792 /* need_space might spring a trap and so we must be careful that the
793 BEGIN_TRAP token is not skipped over. */
798 if (!has_arg() || !get_vunits(&n, 'v'))
799 n = curenv->get_vertical_spacing();
800 while (!tok.newline() && !tok.eof())
810 // the ps4html register is set if we are using -Tps
811 // to generate images for html
812 reg *r = (reg *)number_reg_dictionary.lookup("ps4html");
814 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
815 topdiv->set_next_page_number(n);
821 void save_vertical_space()
824 if (!has_arg() || !get_vunits(&x, 'v'))
825 x = curenv->get_vertical_spacing();
826 if (curdiv->distance_to_next_trap() > x)
833 void output_saved_vertical_space()
835 while (!tok.newline() && !tok.eof())
837 if (saved_space > V0)
838 curdiv->space(saved_space, 1);
845 while (!tok.newline() && !tok.eof())
854 void macro_diversion::set_diversion_trap(symbol s, vunits n)
857 diversion_trap_pos = n;
860 void macro_diversion::clear_diversion_trap()
862 diversion_trap = NULL_SYMBOL;
865 void top_level_diversion::set_diversion_trap(symbol, vunits)
867 error("can't set diversion trap when no current diversion");
870 void top_level_diversion::clear_diversion_trap()
872 error("can't set diversion trap when no current diversion");
875 void diversion_trap()
878 if (has_arg() && get_vunits(&n, 'v')) {
879 symbol s = get_name();
881 curdiv->set_diversion_trap(s, n);
883 curdiv->clear_diversion_trap();
886 curdiv->clear_diversion_trap();
892 symbol s = get_name(1);
895 if (has_arg() && get_vunits(&x, 'v'))
896 topdiv->change_trap(s, x);
898 topdiv->remove_trap(s);
905 topdiv->print_traps();
911 symbol s = get_name();
913 curdiv->marked_place = curdiv->get_vertical_position();
914 else if (curdiv == topdiv)
915 set_number_reg(s, nl_reg_contents);
917 set_number_reg(s, curdiv->get_vertical_position().to_units());
921 // This is truly bizarre. It is documented in the SQ manual.
923 void return_request()
925 vunits dist = curdiv->marked_place - curdiv->get_vertical_position();
927 if (tok.ch() == '-') {
930 if (get_vunits(&x, 'v'))
935 if (get_vunits(&x, 'v'))
936 dist = x >= V0 ? x - curdiv->get_vertical_position() : V0;
944 void vertical_position_traps()
947 if (has_arg() && get_integer(&n))
948 vertical_position_traps_flag = (n != 0);
950 vertical_position_traps_flag = 1;
954 class page_offset_reg : public reg {
956 int get_value(units *);
957 const char *get_string();
960 int page_offset_reg::get_value(units *res)
962 *res = topdiv->get_page_offset().to_units();
966 const char *page_offset_reg::get_string()
968 return i_to_a(topdiv->get_page_offset().to_units());
971 class page_length_reg : public reg {
973 int get_value(units *);
974 const char *get_string();
977 int page_length_reg::get_value(units *res)
979 *res = topdiv->get_page_length().to_units();
983 const char *page_length_reg::get_string()
985 return i_to_a(topdiv->get_page_length().to_units());
988 class vertical_position_reg : public reg {
990 int get_value(units *);
991 const char *get_string();
994 int vertical_position_reg::get_value(units *res)
996 if (curdiv == topdiv && topdiv->before_first_page)
999 *res = curdiv->get_vertical_position().to_units();
1003 const char *vertical_position_reg::get_string()
1005 if (curdiv == topdiv && topdiv->before_first_page)
1008 return i_to_a(curdiv->get_vertical_position().to_units());
1011 class high_water_mark_reg : public reg {
1013 int get_value(units *);
1014 const char *get_string();
1017 int high_water_mark_reg::get_value(units *res)
1019 *res = curdiv->get_high_water_mark().to_units();
1023 const char *high_water_mark_reg::get_string()
1025 return i_to_a(curdiv->get_high_water_mark().to_units());
1028 class distance_to_next_trap_reg : public reg {
1030 int get_value(units *);
1031 const char *get_string();
1034 int distance_to_next_trap_reg::get_value(units *res)
1036 *res = curdiv->distance_to_next_trap().to_units();
1040 const char *distance_to_next_trap_reg::get_string()
1042 return i_to_a(curdiv->distance_to_next_trap().to_units());
1045 class diversion_name_reg : public reg {
1047 const char *get_string();
1050 const char *diversion_name_reg::get_string()
1052 return curdiv->get_diversion_name();
1055 class page_number_reg : public general_reg {
1058 int get_value(units *);
1059 void set_value(units);
1062 page_number_reg::page_number_reg()
1066 void page_number_reg::set_value(units n)
1068 topdiv->set_page_number(n);
1071 int page_number_reg::get_value(units *res)
1073 *res = topdiv->get_page_number();
1077 class next_page_number_reg : public reg {
1079 const char *get_string();
1082 const char *next_page_number_reg::get_string()
1084 return i_to_a(topdiv->get_next_page_number());
1087 class page_ejecting_reg : public reg {
1089 const char *get_string();
1092 const char *page_ejecting_reg::get_string()
1094 return i_to_a(topdiv->get_ejecting());
1097 class constant_vunits_reg : public reg {
1100 constant_vunits_reg(vunits *);
1101 const char *get_string();
1104 constant_vunits_reg::constant_vunits_reg(vunits *q) : p(q)
1108 const char *constant_vunits_reg::get_string()
1110 return i_to_a(p->to_units());
1113 class nl_reg : public variable_reg {
1116 void set_value(units);
1119 nl_reg::nl_reg() : variable_reg(&nl_reg_contents)
1123 void nl_reg::set_value(units n)
1125 variable_reg::set_value(n);
1126 // Setting nl to a negative value when the vertical position in
1127 // the top-level diversion is 0 undoes the top of page transition,
1128 // so that the header macro will be called as if the top of page
1129 // transition hasn't happened. This is used by Larry Wall's
1130 // wrapman program. Setting before_first_page to 2 rather than 1,
1131 // tells top_level_diversion::begin_page not to call
1132 // output_file::begin_page again.
1133 if (n < 0 && topdiv->get_vertical_position() == V0)
1134 topdiv->before_first_page = 2;
1137 class no_space_mode_reg : public reg {
1139 int get_value(units *);
1140 const char *get_string();
1143 int no_space_mode_reg::get_value(units *val)
1145 *val = curdiv->no_space_mode;
1149 const char *no_space_mode_reg::get_string()
1151 return curdiv->no_space_mode ? "1" : "0";
1154 void init_div_requests()
1156 init_request("box", box);
1157 init_request("boxa", box_append);
1158 init_request("bp", begin_page);
1159 init_request("ch", change_trap);
1160 init_request("da", divert_append);
1161 init_request("di", divert);
1162 init_request("dt", diversion_trap);
1163 init_request("fl", flush_output);
1164 init_request("mk", mark);
1165 init_request("ne", need_space);
1166 init_request("ns", no_space);
1167 init_request("os", output_saved_vertical_space);
1168 init_request("pl", page_length);
1169 init_request("pn", page_number);
1170 init_request("po", page_offset);
1171 init_request("ptr", print_traps);
1172 init_request("rs", restore_spacing);
1173 init_request("rt", return_request);
1174 init_request("sp", space_request);
1175 init_request("sv", save_vertical_space);
1176 init_request("vpt", vertical_position_traps);
1177 init_request("wh", when_request);
1178 number_reg_dictionary.define(".a",
1179 new constant_int_reg(&last_post_line_extra_space));
1180 number_reg_dictionary.define(".d", new vertical_position_reg);
1181 number_reg_dictionary.define(".h", new high_water_mark_reg);
1182 number_reg_dictionary.define(".ne",
1183 new constant_vunits_reg(&needed_space));
1184 number_reg_dictionary.define(".ns", new no_space_mode_reg);
1185 number_reg_dictionary.define(".o", new page_offset_reg);
1186 number_reg_dictionary.define(".p", new page_length_reg);
1187 number_reg_dictionary.define(".pe", new page_ejecting_reg);
1188 number_reg_dictionary.define(".pn", new next_page_number_reg);
1189 number_reg_dictionary.define(".t", new distance_to_next_trap_reg);
1190 number_reg_dictionary.define(".trunc",
1191 new constant_vunits_reg(&truncated_space));
1192 number_reg_dictionary.define(".vpt",
1193 new constant_int_reg(&vertical_position_traps_flag));
1194 number_reg_dictionary.define(".z", new diversion_name_reg);
1195 number_reg_dictionary.define("dl", new variable_reg(&dl_reg_contents));
1196 number_reg_dictionary.define("dn", new variable_reg(&dn_reg_contents));
1197 number_reg_dictionary.define("nl", new nl_reg);
1198 number_reg_dictionary.define("%", new page_number_reg);