groff before CVS: release 1.07
[s-roff.git] / troff / div.cc
blob38e338a0cce2b612e455c850355c1d98929a1320
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff 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, or (at your option) any later
10 version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
22 // diversions
24 #include "troff.h"
25 #include "symbol.h"
26 #include "dictionary.h"
27 #include "hvunits.h"
28 #include "env.h"
29 #include "request.h"
30 #include "node.h"
31 #include "token.h"
32 #include "div.h"
33 #include "reg.h"
35 int exit_started = 0; // the exit process has started
36 int done_end_macro = 0; // the end macro (if any) has finished
37 int seen_last_page_ejector = 0; // seen the LAST_PAGE_EJECTOR cookie
38 static int began_page_in_end_macro = 0; // a new page was begun during the end macro
40 static int last_post_line_extra_space = 0; // needed for \n(.a
41 static int nl_reg_contents = -1;
42 static int dl_reg_contents = 0;
43 static int dn_reg_contents = 0;
44 static int vertical_position_traps_flag = 1;
45 static vunits truncated_space;
46 static vunits needed_space;
48 diversion::diversion(symbol s)
49 : nm(s), prev(0), vertical_position(V0), marked_place(V0), high_water_mark(V0)
53 struct vertical_size {
54 vunits pre_extra, post_extra, pre, post;
55 vertical_size(vunits vs, int ls);
58 vertical_size::vertical_size(vunits vs, int ls)
59 : pre_extra(V0), post_extra(V0), pre(vs)
61 if (ls > 1)
62 post = vs*(ls - 1);
63 else
64 post = V0;
67 void node::set_vertical_size(vertical_size *)
71 void extra_size_node::set_vertical_size(vertical_size *v)
73 if (n < V0) {
74 if (-n > v->pre_extra)
75 v->pre_extra = -n;
77 else if (n > v->post_extra)
78 v->post_extra = n;
81 void vertical_size_node::set_vertical_size(vertical_size *v)
83 if (n < V0)
84 v->pre = -n;
85 else
86 v->post = n;
89 top_level_diversion *topdiv;
91 diversion *curdiv;
93 void do_divert(int append)
95 tok.skip();
96 symbol nm = get_name();
97 if (nm.is_null()) {
98 if (curdiv->prev) {
99 diversion *temp = curdiv;
100 curdiv = curdiv->prev;
101 delete temp;
103 else
104 warning(WARN_DI, "diversion stack underflow");
106 else {
107 macro_diversion *md = new macro_diversion(nm, append);
108 md->prev = curdiv;
109 curdiv = md;
111 skip_line();
114 void divert()
116 do_divert(0);
119 void divert_append()
121 do_divert(1);
124 void diversion::need(vunits n)
126 vunits d = distance_to_next_trap();
127 if (d < n) {
128 space(d, 1);
129 truncated_space = -d;
130 needed_space = n;
134 macro_diversion::macro_diversion(symbol s, int append)
135 : diversion(s), max_width(H0)
137 #if 0
138 if (append) {
139 /* We don't allow recursive appends eg:
141 .da a
145 This causes an infinite loop in troff anyway.
146 This is because the user could do
148 .as a foo
150 in the diversion, and this would mess things up royally,
151 since there would be two things appending to the same
152 macro_header.
153 To make it work, we would have to copy the _contents_
154 of the macro into which we were diverting; this doesn't
155 strike me as worthwhile.
156 However,
158 .di a
163 will work and will make `a' contain two copies of what it contained
164 before; in troff, `a' would contain nothing. */
165 request_or_macro *rm
166 = (request_or_macro *)request_dictionary.remove(s);
167 if (!rm || (mac = rm->to_macro()) == 0)
168 mac = new macro;
170 else
171 mac = new macro;
172 #endif
173 // We can now catch the situation described above by comparing
174 // the length of the charlist in the macro_header with the length
175 // stored in the macro. When we detect this, we copy the contents.
176 mac = new macro;
177 if (append) {
178 request_or_macro *rm
179 = (request_or_macro *)request_dictionary.lookup(s);
180 if (rm) {
181 macro *m = rm->to_macro();
182 if (m)
183 *mac = *m;
188 macro_diversion::~macro_diversion()
190 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
191 macro *m = rm ? rm->to_macro() : 0;
192 if (m) {
193 *m = *mac;
194 delete mac;
196 else
197 request_dictionary.define(nm, mac);
198 mac = 0;
199 dl_reg_contents = max_width.to_units();
200 dn_reg_contents = vertical_position.to_units();
203 vunits macro_diversion::distance_to_next_trap()
205 if (!diversion_trap.is_null() && diversion_trap_pos > vertical_position)
206 return diversion_trap_pos - vertical_position;
207 else
208 // Substract vresolution so that vunits::vunits does not overflow.
209 return vunits(INT_MAX - vresolution);
212 void macro_diversion::transparent_output(unsigned char c)
214 mac->append(c);
217 void macro_diversion::transparent_output(node *n)
219 mac->append(n);
222 void macro_diversion::output(node *nd, int retain_size,
223 vunits vs, int ls, hunits width)
225 vertical_size v(vs, ls);
226 while (nd != 0) {
227 nd->set_vertical_size(&v);
228 node *temp = nd;
229 nd = nd->next;
230 if (temp->interpret(mac)) {
231 delete temp;
233 else {
234 #if 1
235 temp->freeze_space();
236 #endif
237 mac->append(temp);
240 if (!v.post_extra.is_zero())
241 last_post_line_extra_space = v.post_extra.to_units();
242 if (!retain_size) {
243 v.pre = vs;
244 v.post = (ls > 1) ? vs*(ls - 1) : V0;
246 if (width > max_width)
247 max_width = width;
248 vunits x = v.pre + v.pre_extra + v.post + v.post_extra;
249 if (vertical_position_traps_flag
250 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
251 && diversion_trap_pos <= vertical_position + x) {
252 vunits trunc = vertical_position + x - diversion_trap_pos;
253 if (trunc > v.post)
254 trunc = v.post;
255 v.post -= trunc;
256 x -= trunc;
257 truncated_space = trunc;
258 spring_trap(diversion_trap);
260 mac->append(new vertical_size_node(-v.pre));
261 mac->append(new vertical_size_node(v.post));
262 mac->append('\n');
263 vertical_position += x;
264 if (vertical_position > high_water_mark)
265 high_water_mark = vertical_position;
268 void macro_diversion::space(vunits n, int)
270 if (vertical_position_traps_flag
271 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
272 && diversion_trap_pos <= vertical_position + n) {
273 truncated_space = vertical_position + n - diversion_trap_pos;
274 n = diversion_trap_pos - vertical_position;
275 spring_trap(diversion_trap);
277 else if (n + vertical_position < V0)
278 n = -vertical_position;
279 mac->append(new diverted_space_node(n));
280 vertical_position += n;
281 if (vertical_position > high_water_mark)
282 high_water_mark = vertical_position;
285 void macro_diversion::copy_file(const char *filename)
287 mac->append(new diverted_copy_file_node(filename));
290 top_level_diversion::top_level_diversion()
291 : page_count(0), have_next_page_number(0), page_length(units_per_inch*11),
292 page_offset(units_per_inch), prev_page_offset(units_per_inch),
293 ejecting_page(0), page_trap_list(0), first_page_begun(0), no_space_mode(0),
294 page_number(0), last_page_count(-1)
298 // find the next trap after pos
300 trap *top_level_diversion::find_next_trap(vunits *next_trap_pos)
302 trap *next_trap = 0;
303 for (trap *pt = page_trap_list; pt != 0; pt = pt->next)
304 if (!pt->nm.is_null()) {
305 if (pt->position >= V0) {
306 if (pt->position > vertical_position
307 && pt->position < page_length
308 && (next_trap == 0 || pt->position < *next_trap_pos)) {
309 next_trap = pt;
310 *next_trap_pos = pt->position;
313 else {
314 vunits pos = pt->position;
315 pos += page_length;
316 if (pos > 0 && pos > vertical_position && (next_trap == 0 || pos < *next_trap_pos)) {
317 next_trap = pt;
318 *next_trap_pos = pos;
322 return next_trap;
325 vunits top_level_diversion::distance_to_next_trap()
327 vunits d;
328 if (!find_next_trap(&d))
329 return page_length - vertical_position;
330 else
331 return d - vertical_position;
334 void top_level_diversion::output(node *nd, int retain_size,
335 vunits vs, int ls, hunits /*width*/)
337 no_space_mode = 0;
338 vunits next_trap_pos;
339 trap *next_trap = find_next_trap(&next_trap_pos);
340 if (!first_page_begun && begin_page())
341 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
342 vertical_size v(vs, ls);
343 for (node *tem = nd; tem != 0; tem = tem->next)
344 tem->set_vertical_size(&v);
345 if (!v.post_extra.is_zero())
346 last_post_line_extra_space = v.post_extra.to_units();
347 if (!retain_size) {
348 v.pre = vs;
349 v.post = (ls > 1) ? vs*(ls - 1) : V0;
351 vertical_position += v.pre;
352 vertical_position += v.pre_extra;
353 the_output->print_line(page_offset, vertical_position, nd,
354 v.pre + v.pre_extra, v.post_extra);
355 vertical_position += v.post_extra;
356 if (vertical_position_traps_flag && vertical_position >= page_length)
357 begin_page();
358 else if (vertical_position_traps_flag
359 && next_trap != 0 && vertical_position >= next_trap_pos) {
360 nl_reg_contents = vertical_position.to_units();
361 truncated_space = v.post;
362 if (vertical_position > high_water_mark)
363 high_water_mark = vertical_position;
364 spring_trap(next_trap->nm);
366 else if (v.post > V0) {
367 vertical_position += v.post;
368 if (vertical_position_traps_flag
369 && next_trap != 0 && vertical_position >= next_trap_pos) {
370 truncated_space = vertical_position - next_trap_pos;
371 vertical_position = next_trap_pos;
372 nl_reg_contents = vertical_position.to_units();
373 if (vertical_position > high_water_mark)
374 high_water_mark = vertical_position;
375 spring_trap(next_trap->nm);
377 else if (vertical_position_traps_flag && vertical_position >= page_length)
378 begin_page();
379 else {
380 nl_reg_contents = vertical_position.to_units();
381 if (vertical_position > high_water_mark)
382 high_water_mark = vertical_position;
385 else {
386 nl_reg_contents = vertical_position.to_units();
387 if (vertical_position > high_water_mark)
388 high_water_mark = vertical_position;
392 void top_level_diversion::transparent_output(unsigned char c)
394 if (!first_page_begun && begin_page())
395 // This can only happen with the transparent() request.
396 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
397 const char *s = asciify(c);
398 while (*s)
399 the_output->transparent_char(*s++);
402 void top_level_diversion::transparent_output(node * /*n*/)
404 error("can't transparently output node at top level");
407 void top_level_diversion::copy_file(const char *filename)
409 if (!first_page_begun && begin_page())
410 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
411 the_output->copy_file(page_offset, vertical_position, filename);
414 void top_level_diversion::space(vunits n, int forced)
416 if (no_space_mode) {
417 if (!forced)
418 return;
419 else
420 no_space_mode = 0;
422 if (!first_page_begun) {
423 if (begin_page()) {
424 // This happens if there's a top of page trap, and the first-page
425 // transition is caused by `'sp'.
426 truncated_space = n > V0 ? n : V0;
427 return;
430 vunits next_trap_pos;
431 trap *next_trap = find_next_trap(&next_trap_pos);
432 vunits y = vertical_position + n;
433 if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) {
434 vertical_position = next_trap_pos;
435 nl_reg_contents = vertical_position.to_units();
436 truncated_space = y - vertical_position;
437 if (vertical_position > high_water_mark)
438 high_water_mark = vertical_position;
439 spring_trap(next_trap->nm);
441 else if (y < V0) {
442 vertical_position = V0;
443 nl_reg_contents = vertical_position.to_units();
445 else if (vertical_position_traps_flag && y >= page_length && n >= V0)
446 begin_page();
447 else {
448 vertical_position = y;
449 nl_reg_contents = vertical_position.to_units();
450 if (vertical_position > high_water_mark)
451 high_water_mark = vertical_position;
455 trap::trap(symbol s, vunits n, trap *p)
456 : nm(s), next(p), position(n)
460 void top_level_diversion::add_trap(symbol nm, vunits pos)
462 trap *first_free_slot = 0;
463 for (trap **p = &page_trap_list; *p; p = &(*p)->next) {
464 if ((*p)->nm.is_null()) {
465 if (first_free_slot == 0)
466 first_free_slot = *p;
468 else if ((*p)->position == pos) {
469 (*p)->nm = nm;
470 return;
473 if (first_free_slot) {
474 first_free_slot->nm = nm;
475 first_free_slot->position = pos;
477 else
478 *p = new trap(nm, pos, 0);
481 void top_level_diversion::remove_trap(symbol nm)
483 for (trap *p = page_trap_list; p; p = p->next)
484 if (p->nm == nm) {
485 p->nm = NULL_SYMBOL;
486 return;
490 void top_level_diversion::remove_trap_at(vunits pos)
492 for (trap *p = page_trap_list; p; p = p->next)
493 if (p->position == pos) {
494 p->nm = NULL_SYMBOL;
495 return;
499 void top_level_diversion::change_trap(symbol nm, vunits pos)
501 for (trap *p = page_trap_list; p; p = p->next)
502 if (p->nm == nm) {
503 p->position = pos;
504 return;
508 void top_level_diversion::print_traps()
510 for (trap *p = page_trap_list; p; p = p->next)
511 if (p->nm.is_null())
512 fprintf(stderr, " empty\n");
513 else
514 fprintf(stderr, "%s\t%d\n", p->nm.contents(), p->position.to_units());
515 fflush(stderr);
518 void end_diversions()
520 while (curdiv != topdiv) {
521 error("automatically ending diversion `%1' on exit",
522 curdiv->nm.contents());
523 diversion *tem = curdiv;
524 curdiv = curdiv->prev;
525 delete tem;
529 NO_RETURN void cleanup_and_exit(int exit_code)
531 if (the_output)
532 delete the_output;
533 exit(exit_code);
536 // returns non-zero if it sprung a top of page trap
538 int top_level_diversion::begin_page()
540 if (exit_started) {
541 if (page_count == last_page_count
542 ? curenv->is_empty()
543 : (done_end_macro && (seen_last_page_ejector || began_page_in_end_macro)))
544 cleanup_and_exit(0);
545 if (!done_end_macro)
546 began_page_in_end_macro = 1;
548 if (!the_output)
549 init_output();
550 ++page_count;
551 if (have_next_page_number) {
552 page_number = next_page_number;
553 have_next_page_number = 0;
555 else if (!first_page_begun)
556 page_number = 1;
557 else
558 page_number++;
559 // spring the top of page trap if there is one
560 vunits next_trap_pos;
561 vertical_position = -vresolution;
562 trap *next_trap = find_next_trap(&next_trap_pos);
563 vertical_position = V0;
564 high_water_mark = V0;
565 first_page_begun = 1;
566 ejecting_page = 0;
567 the_output->begin_page(page_number, page_length);
568 nl_reg_contents = vertical_position.to_units();
569 if (vertical_position_traps_flag && next_trap != 0 && next_trap_pos == V0) {
570 truncated_space = V0;
571 spring_trap(next_trap->nm);
572 return 1;
574 else
575 return 0;
578 void continue_page_eject()
580 if (topdiv->get_ejecting()) {
581 if (curdiv != topdiv)
582 error("can't continue page ejection because of current diversion");
583 else if (!vertical_position_traps_flag)
584 error("can't continue page ejection because vertical position traps disabled");
585 else {
586 push_page_ejector();
587 topdiv->space(topdiv->get_page_length(), 1);
592 void top_level_diversion::set_next_page_number(int n)
594 next_page_number= n;
595 have_next_page_number = 1;
598 int top_level_diversion::get_next_page_number()
600 return have_next_page_number ? next_page_number : page_number + 1;
603 void top_level_diversion::set_page_length(vunits n)
605 page_length = n;
608 diversion::~diversion()
612 void page_offset()
614 hunits n;
615 if (!has_arg() || !get_hunits(&n, 'v', topdiv->page_offset))
616 n = topdiv->prev_page_offset;
617 topdiv->prev_page_offset = topdiv->page_offset;
618 topdiv->page_offset = n;
619 skip_line();
622 void page_length()
624 vunits n;
625 if (has_arg() && get_vunits(&n, 'v', topdiv->get_page_length()))
626 topdiv->set_page_length(n);
627 else
628 topdiv->set_page_length(11*units_per_inch);
629 skip_line();
632 void when_request()
634 vunits n;
635 if (get_vunits(&n, 'v')) {
636 symbol s = get_name();
637 if (s.is_null())
638 topdiv->remove_trap_at(n);
639 else
640 topdiv->add_trap(s, n);
642 skip_line();
645 void begin_page()
647 int got_arg = 0;
648 int n;
649 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
650 got_arg = 1;
651 while (!tok.newline() && !tok.eof())
652 tok.next();
653 if (curdiv == topdiv) {
654 if (!topdiv->first_page_begun) {
655 if (!break_flag) {
656 if (got_arg)
657 topdiv->set_next_page_number(n);
658 if (got_arg || !topdiv->no_space_mode)
659 topdiv->begin_page();
661 else if (topdiv->no_space_mode && !got_arg)
662 topdiv->begin_page();
663 else {
664 /* Given this
666 .wh 0 x
667 .de x
668 .tm \\n%
670 .bp 3
672 troff prints
677 This code makes groff do the same. */
679 push_page_ejector();
680 topdiv->begin_page();
681 if (got_arg)
682 topdiv->set_next_page_number(n);
683 topdiv->set_ejecting();
686 else {
687 push_page_ejector();
688 if (break_flag)
689 curenv->do_break();
690 if (got_arg)
691 topdiv->set_next_page_number(n);
692 if (!(topdiv->no_space_mode && !got_arg))
693 topdiv->set_ejecting();
696 tok.next();
699 void no_space()
701 if (curdiv == topdiv)
702 topdiv->no_space_mode = 1;
703 skip_line();
706 void restore_spacing()
708 if (curdiv == topdiv)
709 topdiv->no_space_mode = 0;
710 skip_line();
713 /* It is necessary to generate a break before before reading the argument,
714 because otherwise arguments using | will be wrong. But if we just
715 generate a break as usual, then the line forced out may spring a trap
716 and thus push a macro onto the input stack before we have had a chance
717 to read the argument to the sp request. We resolve this dilemma by
718 setting, before generating the break, a flag which will postpone the
719 actual pushing of the macro associated with the trap sprung by the
720 outputting of the line forced out by the break till after we have read
721 the argument to the request. If the break did cause a trap to be
722 sprung, then we don't actually do the space. */
724 void space_request()
726 postpone_traps();
727 if (break_flag)
728 curenv->do_break();
729 vunits n;
730 if (!has_arg() || !get_vunits(&n, 'v'))
731 n = curenv->get_vertical_spacing();
732 while (!tok.newline() && !tok.eof())
733 tok.next();
734 if (!unpostpone_traps())
735 curdiv->space(n);
736 else
737 // The line might have had line spacing that was truncated.
738 truncated_space += n;
739 tok.next();
742 void blank_line()
744 curenv->do_break();
745 if (!trap_sprung_flag)
746 curdiv->space(curenv->get_vertical_spacing());
747 else
748 truncated_space += curenv->get_vertical_spacing();
751 /* need_space might spring a trap and so we must be careful that the
752 BEGIN_TRAP token is not skipped over. */
754 void need_space()
756 vunits n;
757 if (!has_arg() || !get_vunits(&n, 'v'))
758 n = curenv->get_vertical_spacing();
759 while (!tok.newline() && !tok.eof())
760 tok.next();
761 curdiv->need(n);
762 tok.next();
765 void page_number()
767 int n;
768 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
769 topdiv->set_next_page_number(n);
770 skip_line();
773 vunits saved_space;
775 void save_vertical_space()
777 vunits x;
778 if (get_vunits(&x, 'v')) {
779 if (curdiv->distance_to_next_trap() > x)
780 curdiv->space(x, 1);
781 else
782 saved_space = x;
784 skip_line();
787 void output_saved_vertical_space()
789 while (!tok.newline() && !tok.eof())
790 tok.next();
791 if (saved_space > V0)
792 curdiv->space(saved_space, 1);
793 saved_space = V0;
794 tok.next();
797 void flush_output()
799 while (!tok.newline() && !tok.eof())
800 tok.next();
801 if (break_flag)
802 curenv->do_break();
803 if (the_output)
804 the_output->flush();
805 tok.next();
808 void macro_diversion::set_diversion_trap(symbol s, vunits n)
810 diversion_trap = s;
811 diversion_trap_pos = n;
814 void macro_diversion::clear_diversion_trap()
816 diversion_trap = NULL_SYMBOL;
819 void top_level_diversion::set_diversion_trap(symbol, vunits)
821 error("can't set diversion trap when no current diversion");
824 void top_level_diversion::clear_diversion_trap()
826 error("can't set diversion trap when no current diversion");
829 void diversion_trap()
831 vunits n;
832 if (has_arg() && get_vunits(&n, 'v')) {
833 symbol s = get_name();
834 if (!s.is_null())
835 curdiv->set_diversion_trap(s, n);
836 else
837 curdiv->clear_diversion_trap();
839 else
840 curdiv->clear_diversion_trap();
841 skip_line();
844 void change_trap()
846 symbol s = get_name(1);
847 if (!s.is_null()) {
848 vunits x;
849 if (has_arg() && get_vunits(&x, 'v'))
850 topdiv->change_trap(s, x);
851 else
852 topdiv->remove_trap(s);
854 skip_line();
857 void print_traps()
859 topdiv->print_traps();
860 skip_line();
863 void mark()
865 symbol s = get_name();
866 if (s.is_null())
867 curdiv->marked_place = curdiv->get_vertical_position();
868 else if (curdiv == topdiv)
869 set_number_reg(s, nl_reg_contents);
870 else
871 set_number_reg(s, curdiv->get_vertical_position().to_units());
872 skip_line();
875 // This is truly bizarre. It is documented in the SQ manual.
877 void return_request()
879 vunits dist = curdiv->marked_place - curdiv->get_vertical_position();
880 if (has_arg()) {
881 if (tok.ch() == '-') {
882 tok.next();
883 vunits x;
884 if (get_vunits(&x, 'v'))
885 dist = -x;
887 else {
888 vunits x;
889 if (get_vunits(&x, 'v'))
890 dist = x >= V0 ? x - curdiv->get_vertical_position() : V0;
893 if (dist < V0)
894 curdiv->space(dist);
895 skip_line();
898 void vertical_position_traps()
900 int n;
901 if (has_arg() && get_integer(&n))
902 vertical_position_traps_flag = (n != 0);
903 else
904 vertical_position_traps_flag = 1;
905 skip_line();
908 class page_offset_reg : public reg {
909 public:
910 int get_value(units *);
911 const char *get_string();
914 int page_offset_reg::get_value(units *res)
916 *res = topdiv->get_page_offset().to_units();
917 return 1;
920 const char *page_offset_reg::get_string()
922 return itoa(topdiv->get_page_offset().to_units());
925 class page_length_reg : public reg {
926 public:
927 int get_value(units *);
928 const char *get_string();
931 int page_length_reg::get_value(units *res)
933 *res = topdiv->get_page_length().to_units();
934 return 1;
937 const char *page_length_reg::get_string()
939 return itoa(topdiv->get_page_length().to_units());
942 class vertical_position_reg : public reg {
943 public:
944 int get_value(units *);
945 const char *get_string();
948 int vertical_position_reg::get_value(units *res)
950 if (curdiv == topdiv && !topdiv->first_page_begun)
951 *res = -1;
952 else
953 *res = curdiv->get_vertical_position().to_units();
954 return 1;
957 const char *vertical_position_reg::get_string()
959 if (curdiv == topdiv && !topdiv->first_page_begun)
960 return "-1";
961 else
962 return itoa(curdiv->get_vertical_position().to_units());
965 class high_water_mark_reg : public reg {
966 public:
967 int get_value(units *);
968 const char *get_string();
971 int high_water_mark_reg::get_value(units *res)
973 *res = curdiv->get_high_water_mark().to_units();
974 return 1;
977 const char *high_water_mark_reg::get_string()
979 return itoa(curdiv->get_high_water_mark().to_units());
982 class distance_to_next_trap_reg : public reg {
983 public:
984 int get_value(units *);
985 const char *get_string();
988 int distance_to_next_trap_reg::get_value(units *res)
990 *res = curdiv->distance_to_next_trap().to_units();
991 return 1;
994 const char *distance_to_next_trap_reg::get_string()
996 return itoa(curdiv->distance_to_next_trap().to_units());
999 class diversion_name_reg : public reg {
1000 public:
1001 const char *get_string();
1004 const char *diversion_name_reg::get_string()
1006 return curdiv->get_diversion_name();
1009 class page_number_reg : public general_reg {
1010 public:
1011 page_number_reg();
1012 int get_value(units *);
1013 void set_value(units);
1016 page_number_reg::page_number_reg()
1020 void page_number_reg::set_value(units n)
1022 topdiv->set_page_number(n);
1025 int page_number_reg::get_value(units *res)
1027 *res = topdiv->get_page_number();
1028 return 1;
1031 class next_page_number_reg : public reg {
1032 public:
1033 const char *get_string();
1036 const char *next_page_number_reg::get_string()
1038 return itoa(topdiv->get_next_page_number());
1041 class page_ejecting_reg : public reg {
1042 public:
1043 const char *get_string();
1046 const char *page_ejecting_reg::get_string()
1048 return itoa(topdiv->get_ejecting());
1051 class constant_vunits_reg : public reg {
1052 vunits *p;
1053 public:
1054 constant_vunits_reg(vunits *);
1055 const char *get_string();
1058 constant_vunits_reg::constant_vunits_reg(vunits *q) : p(q)
1062 const char *constant_vunits_reg::get_string()
1064 return itoa(p->to_units());
1067 void init_div_requests()
1069 init_request("wh", when_request);
1070 init_request("ch", change_trap);
1071 init_request("pl", page_length);
1072 init_request("po", page_offset);
1073 init_request("rs", restore_spacing);
1074 init_request("ns", no_space);
1075 init_request("sp", space_request);
1076 init_request("di", divert);
1077 init_request("da", divert_append);
1078 init_request("bp", begin_page);
1079 init_request("ne", need_space);
1080 init_request("pn", page_number);
1081 init_request("dt", diversion_trap);
1082 init_request("rt", return_request);
1083 init_request("mk", mark);
1084 init_request("sv", save_vertical_space);
1085 init_request("os", output_saved_vertical_space);
1086 init_request("fl", flush_output);
1087 init_request("vpt", vertical_position_traps);
1088 init_request("ptr", print_traps);
1089 number_reg_dictionary.define(".a",
1090 new constant_int_reg(&last_post_line_extra_space));
1091 number_reg_dictionary.define(".z", new diversion_name_reg);
1092 number_reg_dictionary.define(".o", new page_offset_reg);
1093 number_reg_dictionary.define(".p", new page_length_reg);
1094 number_reg_dictionary.define(".d", new vertical_position_reg);
1095 number_reg_dictionary.define(".h", new high_water_mark_reg);
1096 number_reg_dictionary.define(".t", new distance_to_next_trap_reg);
1097 number_reg_dictionary.define("dl", new variable_reg(&dl_reg_contents));
1098 number_reg_dictionary.define("dn", new variable_reg(&dn_reg_contents));
1099 number_reg_dictionary.define("nl", new variable_reg(&nl_reg_contents));
1100 number_reg_dictionary.define(".vpt",
1101 new constant_int_reg(&vertical_position_traps_flag));
1102 number_reg_dictionary.define("%", new page_number_reg);
1103 number_reg_dictionary.define(".pn", new next_page_number_reg);
1104 number_reg_dictionary.define(".trunc",
1105 new constant_vunits_reg(&truncated_space));
1106 number_reg_dictionary.define(".ne",
1107 new constant_vunits_reg(&needed_space));
1108 number_reg_dictionary.define(".pe", new page_ejecting_reg);