2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
27 #include "dictionary.h"
36 int exit_started = 0; // the exit process has started
37 int done_end_macro = 0; // the end macro (if any) has finished
38 int seen_last_page_ejector = 0; // seen the LAST_PAGE_EJECTOR cookie
39 int last_page_number = 0; // if > 0, the number of the last page
41 static int began_page_in_end_macro = 0; // a new page was begun during the end macro
43 static int last_post_line_extra_space = 0; // needed for \n(.a
44 static int nl_reg_contents = -1;
45 static int dl_reg_contents = 0;
46 static int dn_reg_contents = 0;
47 static int vertical_position_traps_flag = 1;
48 static vunits truncated_space;
49 static vunits needed_space;
51 diversion::diversion(symbol s)
52 : prev(0), nm(s), vertical_position(V0), high_water_mark(V0),
53 no_space_mode(0), marked_place(V0)
57 struct vertical_size {
58 vunits pre_extra, post_extra, pre, post;
59 vertical_size(vunits vs, vunits post_vs);
62 vertical_size::vertical_size(vunits vs, vunits post_vs)
63 : pre_extra(V0), post_extra(V0), pre(vs), post(post_vs)
67 void node::set_vertical_size(vertical_size *)
71 void extra_size_node::set_vertical_size(vertical_size *v)
74 if (-n > v->pre_extra)
77 else if (n > v->post_extra)
81 void vertical_size_node::set_vertical_size(vertical_size *v)
89 top_level_diversion *topdiv;
93 void do_divert(int append, int boxing)
96 symbol nm = get_name();
100 curenv->line = curdiv->saved_line;
101 curenv->width_total = curdiv->saved_width_total;
102 curenv->space_total = curdiv->saved_space_total;
103 curenv->saved_indent = curdiv->saved_saved_indent;
104 curenv->target_text_length = curdiv->saved_target_text_length;
105 curenv->prev_line_interrupted = curdiv->saved_prev_line_interrupted;
107 diversion *temp = curdiv;
108 curdiv = curdiv->prev;
112 warning(WARN_DI, "diversion stack underflow");
115 macro_diversion *md = new macro_diversion(nm, append);
119 curdiv->saved_line = curenv->line;
120 curdiv->saved_width_total = curenv->width_total;
121 curdiv->saved_space_total = curenv->space_total;
122 curdiv->saved_saved_indent = curenv->saved_indent;
123 curdiv->saved_target_text_length = curenv->target_text_length;
124 curdiv->saved_prev_line_interrupted = curenv->prev_line_interrupted;
126 curenv->start_line();
152 void diversion::need(vunits n)
154 vunits d = distance_to_next_trap();
157 truncated_space = -d;
162 macro_diversion::macro_diversion(symbol s, int append)
163 : diversion(s), max_width(H0)
167 /* We don't allow recursive appends eg:
173 This causes an infinite loop in troff anyway.
174 This is because the user could do
178 in the diversion, and this would mess things up royally,
179 since there would be two things appending to the same
181 To make it work, we would have to copy the _contents_
182 of the macro into which we were diverting; this doesn't
183 strike me as worthwhile.
191 will work and will make `a' contain two copies of what it contained
192 before; in troff, `a' would contain nothing. */
194 = (request_or_macro *)request_dictionary.remove(s);
195 if (!rm || (mac = rm->to_macro()) == 0)
201 // We can now catch the situation described above by comparing
202 // the length of the charlist in the macro_header with the length
203 // stored in the macro. When we detect this, we copy the contents.
207 = (request_or_macro *)request_dictionary.lookup(s);
209 macro *m = rm->to_macro();
216 macro_diversion::~macro_diversion()
218 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
219 macro *m = rm ? rm->to_macro() : 0;
225 request_dictionary.define(nm, mac);
227 dl_reg_contents = max_width.to_units();
228 dn_reg_contents = vertical_position.to_units();
231 vunits macro_diversion::distance_to_next_trap()
233 if (!diversion_trap.is_null() && diversion_trap_pos > vertical_position)
234 return diversion_trap_pos - vertical_position;
236 // Substract vresolution so that vunits::vunits does not overflow.
237 return vunits(INT_MAX - vresolution);
240 void macro_diversion::transparent_output(unsigned char c)
245 void macro_diversion::transparent_output(node *n)
250 void macro_diversion::output(node *nd, int retain_size,
251 vunits vs, vunits post_vs, hunits width)
254 vertical_size v(vs, post_vs);
256 nd->set_vertical_size(&v);
259 if (temp->interpret(mac)) {
264 temp->freeze_space();
269 last_post_line_extra_space = v.post_extra.to_units();
274 if (width > max_width)
276 vunits x = v.pre + v.pre_extra + v.post + v.post_extra;
277 if (vertical_position_traps_flag
278 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
279 && diversion_trap_pos <= vertical_position + x) {
280 vunits trunc = vertical_position + x - diversion_trap_pos;
285 truncated_space = trunc;
286 spring_trap(diversion_trap);
288 mac->append(new vertical_size_node(-v.pre));
289 mac->append(new vertical_size_node(v.post));
291 vertical_position += x;
292 if (vertical_position - v.post > high_water_mark)
293 high_water_mark = vertical_position - v.post;
296 void macro_diversion::space(vunits n, int)
298 if (vertical_position_traps_flag
299 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
300 && diversion_trap_pos <= vertical_position + n) {
301 truncated_space = vertical_position + n - diversion_trap_pos;
302 n = diversion_trap_pos - vertical_position;
303 spring_trap(diversion_trap);
305 else if (n + vertical_position < V0)
306 n = -vertical_position;
307 mac->append(new diverted_space_node(n));
308 vertical_position += n;
311 void macro_diversion::copy_file(const char *filename)
313 mac->append(new diverted_copy_file_node(filename));
316 top_level_diversion::top_level_diversion()
317 : page_number(0), page_count(0), last_page_count(-1),
318 page_length(units_per_inch*11),
319 prev_page_offset(units_per_inch), page_offset(units_per_inch),
320 page_trap_list(0), have_next_page_number(0),
321 ejecting_page(0), before_first_page(1)
325 // find the next trap after pos
327 trap *top_level_diversion::find_next_trap(vunits *next_trap_pos)
330 for (trap *pt = page_trap_list; pt != 0; pt = pt->next)
331 if (!pt->nm.is_null()) {
332 if (pt->position >= V0) {
333 if (pt->position > vertical_position
334 && pt->position < page_length
335 && (next_trap == 0 || pt->position < *next_trap_pos)) {
337 *next_trap_pos = pt->position;
341 vunits pos = pt->position;
343 if (pos > 0 && pos > vertical_position && (next_trap == 0 || pos < *next_trap_pos)) {
345 *next_trap_pos = pos;
352 vunits top_level_diversion::distance_to_next_trap()
355 if (!find_next_trap(&d))
356 return page_length - vertical_position;
358 return d - vertical_position;
361 void top_level_diversion::output(node *nd, int retain_size,
362 vunits vs, vunits post_vs, hunits width)
365 vunits next_trap_pos;
366 trap *next_trap = find_next_trap(&next_trap_pos);
367 if (before_first_page && begin_page())
368 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
369 vertical_size v(vs, post_vs);
370 for (node *tem = nd; tem != 0; tem = tem->next)
371 tem->set_vertical_size(&v);
372 last_post_line_extra_space = v.post_extra.to_units();
377 vertical_position += v.pre;
378 vertical_position += v.pre_extra;
379 the_output->print_line(page_offset, vertical_position, nd,
380 v.pre + v.pre_extra, v.post_extra, width);
381 vertical_position += v.post_extra;
382 if (vertical_position > high_water_mark)
383 high_water_mark = vertical_position;
384 if (vertical_position_traps_flag && vertical_position >= page_length)
386 else if (vertical_position_traps_flag
387 && next_trap != 0 && vertical_position >= next_trap_pos) {
388 nl_reg_contents = vertical_position.to_units();
389 truncated_space = v.post;
390 spring_trap(next_trap->nm);
392 else if (v.post > V0) {
393 vertical_position += v.post;
394 if (vertical_position_traps_flag
395 && next_trap != 0 && vertical_position >= next_trap_pos) {
396 truncated_space = vertical_position - next_trap_pos;
397 vertical_position = next_trap_pos;
398 nl_reg_contents = vertical_position.to_units();
399 spring_trap(next_trap->nm);
401 else if (vertical_position_traps_flag && vertical_position >= page_length)
404 nl_reg_contents = vertical_position.to_units();
407 nl_reg_contents = vertical_position.to_units();
410 void top_level_diversion::transparent_output(unsigned char c)
412 if (before_first_page && begin_page())
413 // This can only happen with the .output request.
414 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
415 const char *s = asciify(c);
417 the_output->transparent_char(*s++);
420 void top_level_diversion::transparent_output(node * /*n*/)
422 error("can't transparently output node at top level");
425 void top_level_diversion::copy_file(const char *filename)
427 if (before_first_page && begin_page())
428 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
429 the_output->copy_file(page_offset, vertical_position, filename);
432 void top_level_diversion::space(vunits n, int forced)
440 if (before_first_page) {
442 // This happens if there's a top of page trap, and the first-page
443 // transition is caused by `'sp'.
444 truncated_space = n > V0 ? n : V0;
448 vunits next_trap_pos;
449 trap *next_trap = find_next_trap(&next_trap_pos);
450 vunits y = vertical_position + n;
451 if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) {
452 vertical_position = next_trap_pos;
453 nl_reg_contents = vertical_position.to_units();
454 truncated_space = y - vertical_position;
455 spring_trap(next_trap->nm);
458 vertical_position = V0;
459 nl_reg_contents = vertical_position.to_units();
461 else if (vertical_position_traps_flag && y >= page_length && n >= V0)
464 vertical_position = y;
465 nl_reg_contents = vertical_position.to_units();
469 trap::trap(symbol s, vunits n, trap *p)
470 : next(p), position(n), nm(s)
474 void top_level_diversion::add_trap(symbol nm, vunits pos)
476 trap *first_free_slot = 0;
478 for (p = &page_trap_list; *p; p = &(*p)->next) {
479 if ((*p)->nm.is_null()) {
480 if (first_free_slot == 0)
481 first_free_slot = *p;
483 else if ((*p)->position == pos) {
488 if (first_free_slot) {
489 first_free_slot->nm = nm;
490 first_free_slot->position = pos;
493 *p = new trap(nm, pos, 0);
496 void top_level_diversion::remove_trap(symbol nm)
498 for (trap *p = page_trap_list; p; p = p->next)
505 void top_level_diversion::remove_trap_at(vunits pos)
507 for (trap *p = page_trap_list; p; p = p->next)
508 if (p->position == pos) {
514 void top_level_diversion::change_trap(symbol nm, vunits pos)
516 for (trap *p = page_trap_list; p; p = p->next)
523 void top_level_diversion::print_traps()
525 for (trap *p = page_trap_list; p; p = p->next)
527 fprintf(stderr, " empty\n");
529 fprintf(stderr, "%s\t%d\n", p->nm.contents(), p->position.to_units());
533 void end_diversions()
535 while (curdiv != topdiv) {
536 error("automatically ending diversion `%1' on exit",
537 curdiv->nm.contents());
538 diversion *tem = curdiv;
539 curdiv = curdiv->prev;
544 void cleanup_and_exit(int exit_code)
547 the_output->trailer(topdiv->get_page_length());
553 // returns non-zero if it sprung a top of page trap
555 int top_level_diversion::begin_page()
558 if (page_count == last_page_count
560 : (done_end_macro && (seen_last_page_ejector || began_page_in_end_macro)))
563 began_page_in_end_macro = 1;
565 if (last_page_number > 0 && page_number == last_page_number)
570 if (have_next_page_number) {
571 page_number = next_page_number;
572 have_next_page_number = 0;
574 else if (before_first_page == 1)
578 // spring the top of page trap if there is one
579 vunits next_trap_pos;
580 vertical_position = -vresolution;
581 trap *next_trap = find_next_trap(&next_trap_pos);
582 vertical_position = V0;
583 high_water_mark = V0;
585 // If before_first_page was 2, then the top of page transition was undone
586 // using eg .nr nl 0-1. See nl_reg::set_value.
587 if (before_first_page != 2)
588 the_output->begin_page(page_number, page_length);
589 before_first_page = 0;
590 nl_reg_contents = vertical_position.to_units();
591 if (vertical_position_traps_flag && next_trap != 0 && next_trap_pos == V0) {
592 truncated_space = V0;
593 spring_trap(next_trap->nm);
600 void continue_page_eject()
602 if (topdiv->get_ejecting()) {
603 if (curdiv != topdiv)
604 error("can't continue page ejection because of current diversion");
605 else if (!vertical_position_traps_flag)
606 error("can't continue page ejection because vertical position traps disabled");
609 topdiv->space(topdiv->get_page_length(), 1);
614 void top_level_diversion::set_next_page_number(int n)
617 have_next_page_number = 1;
620 int top_level_diversion::get_next_page_number()
622 return have_next_page_number ? next_page_number : page_number + 1;
625 void top_level_diversion::set_page_length(vunits n)
630 diversion::~diversion()
637 // The troff manual says that the default scaling indicator is v,
638 // but it is in fact m: v wouldn't make sense for a horizontally
640 if (!has_arg() || !get_hunits(&n, 'm', topdiv->page_offset))
641 n = topdiv->prev_page_offset;
642 topdiv->prev_page_offset = topdiv->page_offset;
643 topdiv->page_offset = n;
644 curenv->add_html_tag(0, ".po", n.to_units());
651 if (has_arg() && get_vunits(&n, 'v', topdiv->get_page_length()))
652 topdiv->set_page_length(n);
654 topdiv->set_page_length(11*units_per_inch);
661 if (get_vunits(&n, 'v')) {
662 symbol s = get_name();
664 topdiv->remove_trap_at(n);
666 topdiv->add_trap(s, n);
675 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
677 while (!tok.newline() && !tok.eof())
679 if (curdiv == topdiv) {
680 if (topdiv->before_first_page) {
683 topdiv->set_next_page_number(n);
684 if (got_arg || !topdiv->no_space_mode)
685 topdiv->begin_page();
687 else if (topdiv->no_space_mode && !got_arg)
688 topdiv->begin_page();
703 This code makes groff do the same. */
706 topdiv->begin_page();
708 topdiv->set_next_page_number(n);
709 topdiv->set_ejecting();
717 topdiv->set_next_page_number(n);
718 if (!(topdiv->no_space_mode && !got_arg))
719 topdiv->set_ejecting();
727 curdiv->no_space_mode = 1;
731 void restore_spacing()
733 curdiv->no_space_mode = 0;
737 /* It is necessary to generate a break before before reading the argument,
738 because otherwise arguments using | will be wrong. But if we just
739 generate a break as usual, then the line forced out may spring a trap
740 and thus push a macro onto the input stack before we have had a chance
741 to read the argument to the sp request. We resolve this dilemma by
742 setting, before generating the break, a flag which will postpone the
743 actual pushing of the macro associated with the trap sprung by the
744 outputting of the line forced out by the break till after we have read
745 the argument to the request. If the break did cause a trap to be
746 sprung, then we don't actually do the space. */
754 if (!has_arg() || !get_vunits(&n, 'v'))
755 n = curenv->get_vertical_spacing();
756 while (!tok.newline() && !tok.eof())
758 if (!unpostpone_traps() && !curdiv->no_space_mode)
761 // The line might have had line spacing that was truncated.
762 truncated_space += n;
763 curenv->add_html_tag(1, ".sp", n.to_units());
770 if (!trap_sprung_flag && !curdiv->no_space_mode) {
771 curdiv->space(curenv->get_vertical_spacing());
772 curenv->add_html_tag(1, ".sp", 1);
774 truncated_space += curenv->get_vertical_spacing();
777 /* need_space might spring a trap and so we must be careful that the
778 BEGIN_TRAP token is not skipped over. */
783 if (!has_arg() || !get_vunits(&n, 'v'))
784 n = curenv->get_vertical_spacing();
785 while (!tok.newline() && !tok.eof())
795 // the ps4html register is set if we are using -Tps
796 // to generate images for html
797 reg *r = (reg *)number_reg_dictionary.lookup("ps4html");
799 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
800 topdiv->set_next_page_number(n);
806 void save_vertical_space()
809 if (!has_arg() || !get_vunits(&x, 'v'))
810 x = curenv->get_vertical_spacing();
811 if (curdiv->distance_to_next_trap() > x)
818 void output_saved_vertical_space()
820 while (!tok.newline() && !tok.eof())
822 if (saved_space > V0)
823 curdiv->space(saved_space, 1);
830 while (!tok.newline() && !tok.eof())
836 curenv->add_html_tag(1, ".fl");
840 void macro_diversion::set_diversion_trap(symbol s, vunits n)
843 diversion_trap_pos = n;
846 void macro_diversion::clear_diversion_trap()
848 diversion_trap = NULL_SYMBOL;
851 void top_level_diversion::set_diversion_trap(symbol, vunits)
853 error("can't set diversion trap when no current diversion");
856 void top_level_diversion::clear_diversion_trap()
858 error("can't set diversion trap when no current diversion");
861 void diversion_trap()
864 if (has_arg() && get_vunits(&n, 'v')) {
865 symbol s = get_name();
867 curdiv->set_diversion_trap(s, n);
869 curdiv->clear_diversion_trap();
872 curdiv->clear_diversion_trap();
878 symbol s = get_name(1);
881 if (has_arg() && get_vunits(&x, 'v'))
882 topdiv->change_trap(s, x);
884 topdiv->remove_trap(s);
891 topdiv->print_traps();
897 symbol s = get_name();
899 curdiv->marked_place = curdiv->get_vertical_position();
900 else if (curdiv == topdiv)
901 set_number_reg(s, nl_reg_contents);
903 set_number_reg(s, curdiv->get_vertical_position().to_units());
907 // This is truly bizarre. It is documented in the SQ manual.
909 void return_request()
911 vunits dist = curdiv->marked_place - curdiv->get_vertical_position();
913 if (tok.ch() == '-') {
916 if (get_vunits(&x, 'v'))
921 if (get_vunits(&x, 'v'))
922 dist = x >= V0 ? x - curdiv->get_vertical_position() : V0;
930 void vertical_position_traps()
933 if (has_arg() && get_integer(&n))
934 vertical_position_traps_flag = (n != 0);
936 vertical_position_traps_flag = 1;
940 class page_offset_reg : public reg {
942 int get_value(units *);
943 const char *get_string();
946 int page_offset_reg::get_value(units *res)
948 *res = topdiv->get_page_offset().to_units();
952 const char *page_offset_reg::get_string()
954 return i_to_a(topdiv->get_page_offset().to_units());
957 class page_length_reg : public reg {
959 int get_value(units *);
960 const char *get_string();
963 int page_length_reg::get_value(units *res)
965 *res = topdiv->get_page_length().to_units();
969 const char *page_length_reg::get_string()
971 return i_to_a(topdiv->get_page_length().to_units());
974 class vertical_position_reg : public reg {
976 int get_value(units *);
977 const char *get_string();
980 int vertical_position_reg::get_value(units *res)
982 if (curdiv == topdiv && topdiv->before_first_page)
985 *res = curdiv->get_vertical_position().to_units();
989 const char *vertical_position_reg::get_string()
991 if (curdiv == topdiv && topdiv->before_first_page)
994 return i_to_a(curdiv->get_vertical_position().to_units());
997 class high_water_mark_reg : public reg {
999 int get_value(units *);
1000 const char *get_string();
1003 int high_water_mark_reg::get_value(units *res)
1005 *res = curdiv->get_high_water_mark().to_units();
1009 const char *high_water_mark_reg::get_string()
1011 return i_to_a(curdiv->get_high_water_mark().to_units());
1014 class distance_to_next_trap_reg : public reg {
1016 int get_value(units *);
1017 const char *get_string();
1020 int distance_to_next_trap_reg::get_value(units *res)
1022 *res = curdiv->distance_to_next_trap().to_units();
1026 const char *distance_to_next_trap_reg::get_string()
1028 return i_to_a(curdiv->distance_to_next_trap().to_units());
1031 class diversion_name_reg : public reg {
1033 const char *get_string();
1036 const char *diversion_name_reg::get_string()
1038 return curdiv->get_diversion_name();
1041 class page_number_reg : public general_reg {
1044 int get_value(units *);
1045 void set_value(units);
1048 page_number_reg::page_number_reg()
1052 void page_number_reg::set_value(units n)
1054 topdiv->set_page_number(n);
1057 int page_number_reg::get_value(units *res)
1059 *res = topdiv->get_page_number();
1063 class next_page_number_reg : public reg {
1065 const char *get_string();
1068 const char *next_page_number_reg::get_string()
1070 return i_to_a(topdiv->get_next_page_number());
1073 class page_ejecting_reg : public reg {
1075 const char *get_string();
1078 const char *page_ejecting_reg::get_string()
1080 return i_to_a(topdiv->get_ejecting());
1083 class constant_vunits_reg : public reg {
1086 constant_vunits_reg(vunits *);
1087 const char *get_string();
1090 constant_vunits_reg::constant_vunits_reg(vunits *q) : p(q)
1094 const char *constant_vunits_reg::get_string()
1096 return i_to_a(p->to_units());
1099 class nl_reg : public variable_reg {
1102 void set_value(units);
1105 nl_reg::nl_reg() : variable_reg(&nl_reg_contents)
1109 void nl_reg::set_value(units n)
1111 variable_reg::set_value(n);
1112 // Setting nl to a negative value when the vertical position in
1113 // the top-level diversion is 0 undoes the top of page transition,
1114 // so that the header macro will be called as if the top of page
1115 // transition hasn't happened. This is used by Larry Wall's
1116 // wrapman program. Setting before_first_page to 2 rather than 1,
1117 // tells top_level_diversion::begin_page not to call
1118 // output_file::begin_page again.
1119 if (n < 0 && topdiv->get_vertical_position() == V0)
1120 topdiv->before_first_page = 2;
1123 class no_space_mode_reg : public reg {
1125 int get_value(units *);
1126 const char *get_string();
1129 int no_space_mode_reg::get_value(units *val)
1131 *val = curdiv->no_space_mode;
1135 const char *no_space_mode_reg::get_string()
1137 return curdiv->no_space_mode ? "1" : "0";
1140 void init_div_requests()
1142 init_request("wh", when_request);
1143 init_request("ch", change_trap);
1144 init_request("pl", page_length);
1145 init_request("po", page_offset);
1146 init_request("rs", restore_spacing);
1147 init_request("ns", no_space);
1148 init_request("sp", space_request);
1149 init_request("di", divert);
1150 init_request("da", divert_append);
1151 init_request("box", box);
1152 init_request("boxa", box_append);
1153 init_request("bp", begin_page);
1154 init_request("ne", need_space);
1155 init_request("pn", page_number);
1156 init_request("dt", diversion_trap);
1157 init_request("rt", return_request);
1158 init_request("mk", mark);
1159 init_request("sv", save_vertical_space);
1160 init_request("os", output_saved_vertical_space);
1161 init_request("fl", flush_output);
1162 init_request("vpt", vertical_position_traps);
1163 init_request("ptr", print_traps);
1164 number_reg_dictionary.define(".a",
1165 new constant_int_reg(&last_post_line_extra_space));
1166 number_reg_dictionary.define(".z", new diversion_name_reg);
1167 number_reg_dictionary.define(".o", new page_offset_reg);
1168 number_reg_dictionary.define(".p", new page_length_reg);
1169 number_reg_dictionary.define(".ns", new no_space_mode_reg);
1170 number_reg_dictionary.define(".d", new vertical_position_reg);
1171 number_reg_dictionary.define(".h", new high_water_mark_reg);
1172 number_reg_dictionary.define(".t", new distance_to_next_trap_reg);
1173 number_reg_dictionary.define("dl", new variable_reg(&dl_reg_contents));
1174 number_reg_dictionary.define("dn", new variable_reg(&dn_reg_contents));
1175 number_reg_dictionary.define("nl", new nl_reg);
1176 number_reg_dictionary.define(".vpt",
1177 new constant_int_reg(&vertical_position_traps_flag));
1178 number_reg_dictionary.define("%", new page_number_reg);
1179 number_reg_dictionary.define(".pn", new next_page_number_reg);
1180 number_reg_dictionary.define(".trunc",
1181 new constant_vunits_reg(&truncated_space));
1182 number_reg_dictionary.define(".ne",
1183 new constant_vunits_reg(&needed_space));
1184 number_reg_dictionary.define(".pe", new page_ejecting_reg);