For security reasons, don't use the current directory but the home
[s-roff.git] / src / roff / troff / div.cc
blob01ee2c612c90d4464f33a10e356d319ccc55f93a
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 int last_page_number = 0; // if > 0, the number of the last page
39 // specified with -o
40 static int began_page_in_end_macro = 0; // a new page was begun during the end macro
42 static int last_post_line_extra_space = 0; // needed for \n(.a
43 static int nl_reg_contents = -1;
44 static int dl_reg_contents = 0;
45 static int dn_reg_contents = 0;
46 static int vertical_position_traps_flag = 1;
47 static vunits truncated_space;
48 static vunits needed_space;
50 diversion::diversion(symbol s)
51 : prev(0), nm(s), vertical_position(V0), high_water_mark(V0), marked_place(V0)
55 struct vertical_size {
56 vunits pre_extra, post_extra, pre, post;
57 vertical_size(vunits vs, vunits post_vs);
60 vertical_size::vertical_size(vunits vs, vunits post_vs)
61 : pre_extra(V0), post_extra(V0), pre(vs), post(post_vs)
65 void node::set_vertical_size(vertical_size *)
69 void extra_size_node::set_vertical_size(vertical_size *v)
71 if (n < V0) {
72 if (-n > v->pre_extra)
73 v->pre_extra = -n;
75 else if (n > v->post_extra)
76 v->post_extra = n;
79 void vertical_size_node::set_vertical_size(vertical_size *v)
81 if (n < V0)
82 v->pre = -n;
83 else
84 v->post = n;
87 top_level_diversion *topdiv;
89 diversion *curdiv;
91 void do_divert(int append)
93 tok.skip();
94 symbol nm = get_name();
95 if (nm.is_null()) {
96 if (curdiv->prev) {
97 diversion *temp = curdiv;
98 curdiv = curdiv->prev;
99 delete temp;
101 else
102 warning(WARN_DI, "diversion stack underflow");
104 else {
105 macro_diversion *md = new macro_diversion(nm, append);
106 md->prev = curdiv;
107 curdiv = md;
109 skip_line();
112 void divert()
114 do_divert(0);
117 void divert_append()
119 do_divert(1);
122 void diversion::need(vunits n)
124 vunits d = distance_to_next_trap();
125 if (d < n) {
126 space(d, 1);
127 truncated_space = -d;
128 needed_space = n;
132 macro_diversion::macro_diversion(symbol s, int append)
133 : diversion(s), max_width(H0)
135 #if 0
136 if (append) {
137 /* We don't allow recursive appends eg:
139 .da a
143 This causes an infinite loop in troff anyway.
144 This is because the user could do
146 .as a foo
148 in the diversion, and this would mess things up royally,
149 since there would be two things appending to the same
150 macro_header.
151 To make it work, we would have to copy the _contents_
152 of the macro into which we were diverting; this doesn't
153 strike me as worthwhile.
154 However,
156 .di a
161 will work and will make `a' contain two copies of what it contained
162 before; in troff, `a' would contain nothing. */
163 request_or_macro *rm
164 = (request_or_macro *)request_dictionary.remove(s);
165 if (!rm || (mac = rm->to_macro()) == 0)
166 mac = new macro;
168 else
169 mac = new macro;
170 #endif
171 // We can now catch the situation described above by comparing
172 // the length of the charlist in the macro_header with the length
173 // stored in the macro. When we detect this, we copy the contents.
174 mac = new macro;
175 if (append) {
176 request_or_macro *rm
177 = (request_or_macro *)request_dictionary.lookup(s);
178 if (rm) {
179 macro *m = rm->to_macro();
180 if (m)
181 *mac = *m;
186 macro_diversion::~macro_diversion()
188 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
189 macro *m = rm ? rm->to_macro() : 0;
190 if (m) {
191 *m = *mac;
192 delete mac;
194 else
195 request_dictionary.define(nm, mac);
196 mac = 0;
197 dl_reg_contents = max_width.to_units();
198 dn_reg_contents = vertical_position.to_units();
201 vunits macro_diversion::distance_to_next_trap()
203 if (!diversion_trap.is_null() && diversion_trap_pos > vertical_position)
204 return diversion_trap_pos - vertical_position;
205 else
206 // Substract vresolution so that vunits::vunits does not overflow.
207 return vunits(INT_MAX - vresolution);
210 void macro_diversion::transparent_output(unsigned char c)
212 mac->append(c);
215 void macro_diversion::transparent_output(node *n)
217 mac->append(n);
220 void macro_diversion::output(node *nd, int retain_size,
221 vunits vs, vunits post_vs, hunits width)
223 vertical_size v(vs, post_vs);
224 while (nd != 0) {
225 nd->set_vertical_size(&v);
226 node *temp = nd;
227 nd = nd->next;
228 if (temp->interpret(mac)) {
229 delete temp;
231 else {
232 #if 1
233 temp->freeze_space();
234 #endif
235 mac->append(temp);
238 last_post_line_extra_space = v.post_extra.to_units();
239 if (!retain_size) {
240 v.pre = vs;
241 v.post = post_vs;
243 if (width > max_width)
244 max_width = width;
245 vunits x = v.pre + v.pre_extra + v.post + v.post_extra;
246 if (vertical_position_traps_flag
247 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
248 && diversion_trap_pos <= vertical_position + x) {
249 vunits trunc = vertical_position + x - diversion_trap_pos;
250 if (trunc > v.post)
251 trunc = v.post;
252 v.post -= trunc;
253 x -= trunc;
254 truncated_space = trunc;
255 spring_trap(diversion_trap);
257 mac->append(new vertical_size_node(-v.pre));
258 mac->append(new vertical_size_node(v.post));
259 mac->append('\n');
260 vertical_position += x;
261 if (vertical_position - v.post > high_water_mark)
262 high_water_mark = vertical_position - v.post;
265 void macro_diversion::space(vunits n, int)
267 if (vertical_position_traps_flag
268 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
269 && diversion_trap_pos <= vertical_position + n) {
270 truncated_space = vertical_position + n - diversion_trap_pos;
271 n = diversion_trap_pos - vertical_position;
272 spring_trap(diversion_trap);
274 else if (n + vertical_position < V0)
275 n = -vertical_position;
276 mac->append(new diverted_space_node(n));
277 vertical_position += n;
280 void macro_diversion::copy_file(const char *filename)
282 mac->append(new diverted_copy_file_node(filename));
285 top_level_diversion::top_level_diversion()
286 : page_number(0), page_count(0), last_page_count(-1),
287 page_length(units_per_inch*11),
288 prev_page_offset(units_per_inch), page_offset(units_per_inch),
289 page_trap_list(0), have_next_page_number(0),
290 ejecting_page(0), before_first_page(1), no_space_mode(0)
294 // find the next trap after pos
296 trap *top_level_diversion::find_next_trap(vunits *next_trap_pos)
298 trap *next_trap = 0;
299 for (trap *pt = page_trap_list; pt != 0; pt = pt->next)
300 if (!pt->nm.is_null()) {
301 if (pt->position >= V0) {
302 if (pt->position > vertical_position
303 && pt->position < page_length
304 && (next_trap == 0 || pt->position < *next_trap_pos)) {
305 next_trap = pt;
306 *next_trap_pos = pt->position;
309 else {
310 vunits pos = pt->position;
311 pos += page_length;
312 if (pos > 0 && pos > vertical_position && (next_trap == 0 || pos < *next_trap_pos)) {
313 next_trap = pt;
314 *next_trap_pos = pos;
318 return next_trap;
321 vunits top_level_diversion::distance_to_next_trap()
323 vunits d;
324 if (!find_next_trap(&d))
325 return page_length - vertical_position;
326 else
327 return d - vertical_position;
330 void top_level_diversion::output(node *nd, int retain_size,
331 vunits vs, vunits post_vs, hunits width)
333 no_space_mode = 0;
334 vunits next_trap_pos;
335 trap *next_trap = find_next_trap(&next_trap_pos);
336 if (before_first_page && begin_page())
337 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
338 vertical_size v(vs, post_vs);
339 for (node *tem = nd; tem != 0; tem = tem->next)
340 tem->set_vertical_size(&v);
341 last_post_line_extra_space = v.post_extra.to_units();
342 if (!retain_size) {
343 v.pre = vs;
344 v.post = post_vs;
346 vertical_position += v.pre;
347 vertical_position += v.pre_extra;
348 the_output->print_line(page_offset, vertical_position, nd,
349 v.pre + v.pre_extra, v.post_extra, width);
350 vertical_position += v.post_extra;
351 if (vertical_position > high_water_mark)
352 high_water_mark = vertical_position;
353 if (vertical_position_traps_flag && vertical_position >= page_length)
354 begin_page();
355 else if (vertical_position_traps_flag
356 && next_trap != 0 && vertical_position >= next_trap_pos) {
357 nl_reg_contents = vertical_position.to_units();
358 truncated_space = v.post;
359 spring_trap(next_trap->nm);
361 else if (v.post > V0) {
362 vertical_position += v.post;
363 if (vertical_position_traps_flag
364 && next_trap != 0 && vertical_position >= next_trap_pos) {
365 truncated_space = vertical_position - next_trap_pos;
366 vertical_position = next_trap_pos;
367 nl_reg_contents = vertical_position.to_units();
368 spring_trap(next_trap->nm);
370 else if (vertical_position_traps_flag && vertical_position >= page_length)
371 begin_page();
372 else
373 nl_reg_contents = vertical_position.to_units();
375 else
376 nl_reg_contents = vertical_position.to_units();
379 void top_level_diversion::transparent_output(unsigned char c)
381 if (before_first_page && begin_page())
382 // This can only happen with the transparent() request.
383 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
384 const char *s = asciify(c);
385 while (*s)
386 the_output->transparent_char(*s++);
389 void top_level_diversion::transparent_output(node * /*n*/)
391 error("can't transparently output node at top level");
394 void top_level_diversion::copy_file(const char *filename)
396 if (before_first_page && begin_page())
397 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
398 the_output->copy_file(page_offset, vertical_position, filename);
401 void top_level_diversion::space(vunits n, int forced)
403 if (no_space_mode) {
404 if (!forced)
405 return;
406 else
407 no_space_mode = 0;
409 if (before_first_page) {
410 if (begin_page()) {
411 // This happens if there's a top of page trap, and the first-page
412 // transition is caused by `'sp'.
413 truncated_space = n > V0 ? n : V0;
414 return;
417 vunits next_trap_pos;
418 trap *next_trap = find_next_trap(&next_trap_pos);
419 vunits y = vertical_position + n;
420 if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) {
421 vertical_position = next_trap_pos;
422 nl_reg_contents = vertical_position.to_units();
423 truncated_space = y - vertical_position;
424 spring_trap(next_trap->nm);
426 else if (y < V0) {
427 vertical_position = V0;
428 nl_reg_contents = vertical_position.to_units();
430 else if (vertical_position_traps_flag && y >= page_length && n >= V0)
431 begin_page();
432 else {
433 vertical_position = y;
434 nl_reg_contents = vertical_position.to_units();
438 trap::trap(symbol s, vunits n, trap *p)
439 : next(p), position(n), nm(s)
443 void top_level_diversion::add_trap(symbol nm, vunits pos)
445 trap *first_free_slot = 0;
446 trap **p;
447 for (p = &page_trap_list; *p; p = &(*p)->next) {
448 if ((*p)->nm.is_null()) {
449 if (first_free_slot == 0)
450 first_free_slot = *p;
452 else if ((*p)->position == pos) {
453 (*p)->nm = nm;
454 return;
457 if (first_free_slot) {
458 first_free_slot->nm = nm;
459 first_free_slot->position = pos;
461 else
462 *p = new trap(nm, pos, 0);
465 void top_level_diversion::remove_trap(symbol nm)
467 for (trap *p = page_trap_list; p; p = p->next)
468 if (p->nm == nm) {
469 p->nm = NULL_SYMBOL;
470 return;
474 void top_level_diversion::remove_trap_at(vunits pos)
476 for (trap *p = page_trap_list; p; p = p->next)
477 if (p->position == pos) {
478 p->nm = NULL_SYMBOL;
479 return;
483 void top_level_diversion::change_trap(symbol nm, vunits pos)
485 for (trap *p = page_trap_list; p; p = p->next)
486 if (p->nm == nm) {
487 p->position = pos;
488 return;
492 void top_level_diversion::print_traps()
494 for (trap *p = page_trap_list; p; p = p->next)
495 if (p->nm.is_null())
496 fprintf(stderr, " empty\n");
497 else
498 fprintf(stderr, "%s\t%d\n", p->nm.contents(), p->position.to_units());
499 fflush(stderr);
502 void end_diversions()
504 while (curdiv != topdiv) {
505 error("automatically ending diversion `%1' on exit",
506 curdiv->nm.contents());
507 diversion *tem = curdiv;
508 curdiv = curdiv->prev;
509 delete tem;
513 void cleanup_and_exit(int exit_code)
515 if (the_output) {
516 the_output->trailer(topdiv->get_page_length());
517 delete the_output;
519 exit(exit_code);
522 // returns non-zero if it sprung a top of page trap
524 int top_level_diversion::begin_page()
526 if (exit_started) {
527 if (page_count == last_page_count
528 ? curenv->is_empty()
529 : (done_end_macro && (seen_last_page_ejector || began_page_in_end_macro)))
530 cleanup_and_exit(0);
531 if (!done_end_macro)
532 began_page_in_end_macro = 1;
534 if (last_page_number > 0 && page_number == last_page_number)
535 cleanup_and_exit(0);
536 if (!the_output)
537 init_output();
538 ++page_count;
539 if (have_next_page_number) {
540 page_number = next_page_number;
541 have_next_page_number = 0;
543 else if (before_first_page == 1)
544 page_number = 1;
545 else
546 page_number++;
547 // spring the top of page trap if there is one
548 vunits next_trap_pos;
549 vertical_position = -vresolution;
550 trap *next_trap = find_next_trap(&next_trap_pos);
551 vertical_position = V0;
552 high_water_mark = V0;
553 ejecting_page = 0;
554 // If before_first_page was 2, then the top of page transition was undone
555 // using eg .nr nl 0-1. See nl_reg::set_value.
556 if (before_first_page != 2)
557 the_output->begin_page(page_number, page_length);
558 before_first_page = 0;
559 nl_reg_contents = vertical_position.to_units();
560 if (vertical_position_traps_flag && next_trap != 0 && next_trap_pos == V0) {
561 truncated_space = V0;
562 spring_trap(next_trap->nm);
563 return 1;
565 else
566 return 0;
569 void continue_page_eject()
571 if (topdiv->get_ejecting()) {
572 if (curdiv != topdiv)
573 error("can't continue page ejection because of current diversion");
574 else if (!vertical_position_traps_flag)
575 error("can't continue page ejection because vertical position traps disabled");
576 else {
577 push_page_ejector();
578 topdiv->space(topdiv->get_page_length(), 1);
583 void top_level_diversion::set_next_page_number(int n)
585 next_page_number= n;
586 have_next_page_number = 1;
589 int top_level_diversion::get_next_page_number()
591 return have_next_page_number ? next_page_number : page_number + 1;
594 void top_level_diversion::set_page_length(vunits n)
596 page_length = n;
599 diversion::~diversion()
603 void page_offset()
605 hunits n;
606 // The troff manual says that the default scaling indicator is v,
607 // but it is in fact m: v wouldn't make sense for a horizontally
608 // oriented request.
609 if (!has_arg() || !get_hunits(&n, 'm', topdiv->page_offset))
610 n = topdiv->prev_page_offset;
611 topdiv->prev_page_offset = topdiv->page_offset;
612 topdiv->page_offset = n;
613 skip_line();
616 void page_length()
618 vunits n;
619 if (has_arg() && get_vunits(&n, 'v', topdiv->get_page_length()))
620 topdiv->set_page_length(n);
621 else
622 topdiv->set_page_length(11*units_per_inch);
623 skip_line();
626 void when_request()
628 vunits n;
629 if (get_vunits(&n, 'v')) {
630 symbol s = get_name();
631 if (s.is_null())
632 topdiv->remove_trap_at(n);
633 else
634 topdiv->add_trap(s, n);
636 skip_line();
639 void begin_page()
641 int got_arg = 0;
642 int n;
643 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
644 got_arg = 1;
645 while (!tok.newline() && !tok.eof())
646 tok.next();
647 if (curdiv == topdiv) {
648 if (topdiv->before_first_page) {
649 if (!break_flag) {
650 if (got_arg)
651 topdiv->set_next_page_number(n);
652 if (got_arg || !topdiv->no_space_mode)
653 topdiv->begin_page();
655 else if (topdiv->no_space_mode && !got_arg)
656 topdiv->begin_page();
657 else {
658 /* Given this
660 .wh 0 x
661 .de x
662 .tm \\n%
664 .bp 3
666 troff prints
671 This code makes groff do the same. */
673 push_page_ejector();
674 topdiv->begin_page();
675 if (got_arg)
676 topdiv->set_next_page_number(n);
677 topdiv->set_ejecting();
680 else {
681 push_page_ejector();
682 if (break_flag)
683 curenv->do_break();
684 if (got_arg)
685 topdiv->set_next_page_number(n);
686 if (!(topdiv->no_space_mode && !got_arg))
687 topdiv->set_ejecting();
690 tok.next();
693 void no_space()
695 if (curdiv == topdiv)
696 topdiv->no_space_mode = 1;
697 skip_line();
700 void restore_spacing()
702 if (curdiv == topdiv)
703 topdiv->no_space_mode = 0;
704 skip_line();
707 /* It is necessary to generate a break before before reading the argument,
708 because otherwise arguments using | will be wrong. But if we just
709 generate a break as usual, then the line forced out may spring a trap
710 and thus push a macro onto the input stack before we have had a chance
711 to read the argument to the sp request. We resolve this dilemma by
712 setting, before generating the break, a flag which will postpone the
713 actual pushing of the macro associated with the trap sprung by the
714 outputting of the line forced out by the break till after we have read
715 the argument to the request. If the break did cause a trap to be
716 sprung, then we don't actually do the space. */
718 void space_request()
720 postpone_traps();
721 if (break_flag)
722 curenv->do_break();
723 vunits n;
724 if (!has_arg() || !get_vunits(&n, 'v'))
725 n = curenv->get_vertical_spacing();
726 while (!tok.newline() && !tok.eof())
727 tok.next();
728 if (!unpostpone_traps())
729 curdiv->space(n);
730 else
731 // The line might have had line spacing that was truncated.
732 truncated_space += n;
733 tok.next();
736 void blank_line()
738 curenv->do_break();
739 if (!trap_sprung_flag)
740 curdiv->space(curenv->get_vertical_spacing());
741 else
742 truncated_space += curenv->get_vertical_spacing();
745 /* need_space might spring a trap and so we must be careful that the
746 BEGIN_TRAP token is not skipped over. */
748 void need_space()
750 vunits n;
751 if (!has_arg() || !get_vunits(&n, 'v'))
752 n = curenv->get_vertical_spacing();
753 while (!tok.newline() && !tok.eof())
754 tok.next();
755 curdiv->need(n);
756 tok.next();
759 void page_number()
761 int n;
762 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
763 topdiv->set_next_page_number(n);
764 skip_line();
767 vunits saved_space;
769 void save_vertical_space()
771 vunits x;
772 if (get_vunits(&x, 'v')) {
773 if (curdiv->distance_to_next_trap() > x)
774 curdiv->space(x, 1);
775 else
776 saved_space = x;
778 skip_line();
781 void output_saved_vertical_space()
783 while (!tok.newline() && !tok.eof())
784 tok.next();
785 if (saved_space > V0)
786 curdiv->space(saved_space, 1);
787 saved_space = V0;
788 tok.next();
791 void flush_output()
793 while (!tok.newline() && !tok.eof())
794 tok.next();
795 if (break_flag)
796 curenv->do_break();
797 if (the_output)
798 the_output->flush();
799 tok.next();
802 void macro_diversion::set_diversion_trap(symbol s, vunits n)
804 diversion_trap = s;
805 diversion_trap_pos = n;
808 void macro_diversion::clear_diversion_trap()
810 diversion_trap = NULL_SYMBOL;
813 void top_level_diversion::set_diversion_trap(symbol, vunits)
815 error("can't set diversion trap when no current diversion");
818 void top_level_diversion::clear_diversion_trap()
820 error("can't set diversion trap when no current diversion");
823 void diversion_trap()
825 vunits n;
826 if (has_arg() && get_vunits(&n, 'v')) {
827 symbol s = get_name();
828 if (!s.is_null())
829 curdiv->set_diversion_trap(s, n);
830 else
831 curdiv->clear_diversion_trap();
833 else
834 curdiv->clear_diversion_trap();
835 skip_line();
838 void change_trap()
840 symbol s = get_name(1);
841 if (!s.is_null()) {
842 vunits x;
843 if (has_arg() && get_vunits(&x, 'v'))
844 topdiv->change_trap(s, x);
845 else
846 topdiv->remove_trap(s);
848 skip_line();
851 void print_traps()
853 topdiv->print_traps();
854 skip_line();
857 void mark()
859 symbol s = get_name();
860 if (s.is_null())
861 curdiv->marked_place = curdiv->get_vertical_position();
862 else if (curdiv == topdiv)
863 set_number_reg(s, nl_reg_contents);
864 else
865 set_number_reg(s, curdiv->get_vertical_position().to_units());
866 skip_line();
869 // This is truly bizarre. It is documented in the SQ manual.
871 void return_request()
873 vunits dist = curdiv->marked_place - curdiv->get_vertical_position();
874 if (has_arg()) {
875 if (tok.ch() == '-') {
876 tok.next();
877 vunits x;
878 if (get_vunits(&x, 'v'))
879 dist = -x;
881 else {
882 vunits x;
883 if (get_vunits(&x, 'v'))
884 dist = x >= V0 ? x - curdiv->get_vertical_position() : V0;
887 if (dist < V0)
888 curdiv->space(dist);
889 skip_line();
892 void vertical_position_traps()
894 int n;
895 if (has_arg() && get_integer(&n))
896 vertical_position_traps_flag = (n != 0);
897 else
898 vertical_position_traps_flag = 1;
899 skip_line();
902 class page_offset_reg : public reg {
903 public:
904 int get_value(units *);
905 const char *get_string();
908 int page_offset_reg::get_value(units *res)
910 *res = topdiv->get_page_offset().to_units();
911 return 1;
914 const char *page_offset_reg::get_string()
916 return i_to_a(topdiv->get_page_offset().to_units());
919 class page_length_reg : public reg {
920 public:
921 int get_value(units *);
922 const char *get_string();
925 int page_length_reg::get_value(units *res)
927 *res = topdiv->get_page_length().to_units();
928 return 1;
931 const char *page_length_reg::get_string()
933 return i_to_a(topdiv->get_page_length().to_units());
936 class vertical_position_reg : public reg {
937 public:
938 int get_value(units *);
939 const char *get_string();
942 int vertical_position_reg::get_value(units *res)
944 if (curdiv == topdiv && topdiv->before_first_page)
945 *res = -1;
946 else
947 *res = curdiv->get_vertical_position().to_units();
948 return 1;
951 const char *vertical_position_reg::get_string()
953 if (curdiv == topdiv && topdiv->before_first_page)
954 return "-1";
955 else
956 return i_to_a(curdiv->get_vertical_position().to_units());
959 class high_water_mark_reg : public reg {
960 public:
961 int get_value(units *);
962 const char *get_string();
965 int high_water_mark_reg::get_value(units *res)
967 *res = curdiv->get_high_water_mark().to_units();
968 return 1;
971 const char *high_water_mark_reg::get_string()
973 return i_to_a(curdiv->get_high_water_mark().to_units());
976 class distance_to_next_trap_reg : public reg {
977 public:
978 int get_value(units *);
979 const char *get_string();
982 int distance_to_next_trap_reg::get_value(units *res)
984 *res = curdiv->distance_to_next_trap().to_units();
985 return 1;
988 const char *distance_to_next_trap_reg::get_string()
990 return i_to_a(curdiv->distance_to_next_trap().to_units());
993 class diversion_name_reg : public reg {
994 public:
995 const char *get_string();
998 const char *diversion_name_reg::get_string()
1000 return curdiv->get_diversion_name();
1003 class page_number_reg : public general_reg {
1004 public:
1005 page_number_reg();
1006 int get_value(units *);
1007 void set_value(units);
1010 page_number_reg::page_number_reg()
1014 void page_number_reg::set_value(units n)
1016 topdiv->set_page_number(n);
1019 int page_number_reg::get_value(units *res)
1021 *res = topdiv->get_page_number();
1022 return 1;
1025 class next_page_number_reg : public reg {
1026 public:
1027 const char *get_string();
1030 const char *next_page_number_reg::get_string()
1032 return i_to_a(topdiv->get_next_page_number());
1035 class page_ejecting_reg : public reg {
1036 public:
1037 const char *get_string();
1040 const char *page_ejecting_reg::get_string()
1042 return i_to_a(topdiv->get_ejecting());
1045 class constant_vunits_reg : public reg {
1046 vunits *p;
1047 public:
1048 constant_vunits_reg(vunits *);
1049 const char *get_string();
1052 constant_vunits_reg::constant_vunits_reg(vunits *q) : p(q)
1056 const char *constant_vunits_reg::get_string()
1058 return i_to_a(p->to_units());
1061 class nl_reg : public variable_reg {
1062 public:
1063 nl_reg();
1064 void set_value(units);
1067 nl_reg::nl_reg() : variable_reg(&nl_reg_contents)
1071 void nl_reg::set_value(units n)
1073 variable_reg::set_value(n);
1074 // Setting nl to a negative value when the vertical position in
1075 // the top-level diversion is 0 undoes the top of page transition,
1076 // so that the header macro will be called as if the top of page
1077 // transition hasn't happened. This is used by Larry Wall's
1078 // wrapman program. Setting before_first_page to 2 rather than 1,
1079 // tells top_level_diversion::begin_page not to call
1080 // output_file::begin_page again.
1081 if (n < 0 && topdiv->get_vertical_position() == V0)
1082 topdiv->before_first_page = 2;
1085 void init_div_requests()
1087 init_request("wh", when_request);
1088 init_request("ch", change_trap);
1089 init_request("pl", page_length);
1090 init_request("po", page_offset);
1091 init_request("rs", restore_spacing);
1092 init_request("ns", no_space);
1093 init_request("sp", space_request);
1094 init_request("di", divert);
1095 init_request("da", divert_append);
1096 init_request("bp", begin_page);
1097 init_request("ne", need_space);
1098 init_request("pn", page_number);
1099 init_request("dt", diversion_trap);
1100 init_request("rt", return_request);
1101 init_request("mk", mark);
1102 init_request("sv", save_vertical_space);
1103 init_request("os", output_saved_vertical_space);
1104 init_request("fl", flush_output);
1105 init_request("vpt", vertical_position_traps);
1106 init_request("ptr", print_traps);
1107 number_reg_dictionary.define(".a",
1108 new constant_int_reg(&last_post_line_extra_space));
1109 number_reg_dictionary.define(".z", new diversion_name_reg);
1110 number_reg_dictionary.define(".o", new page_offset_reg);
1111 number_reg_dictionary.define(".p", new page_length_reg);
1112 number_reg_dictionary.define(".d", new vertical_position_reg);
1113 number_reg_dictionary.define(".h", new high_water_mark_reg);
1114 number_reg_dictionary.define(".t", new distance_to_next_trap_reg);
1115 number_reg_dictionary.define("dl", new variable_reg(&dl_reg_contents));
1116 number_reg_dictionary.define("dn", new variable_reg(&dn_reg_contents));
1117 number_reg_dictionary.define("nl", new nl_reg);
1118 number_reg_dictionary.define(".vpt",
1119 new constant_int_reg(&vertical_position_traps_flag));
1120 number_reg_dictionary.define("%", new page_number_reg);
1121 number_reg_dictionary.define(".pn", new next_page_number_reg);
1122 number_reg_dictionary.define(".trunc",
1123 new constant_vunits_reg(&truncated_space));
1124 number_reg_dictionary.define(".ne",
1125 new constant_vunits_reg(&needed_space));
1126 number_reg_dictionary.define(".pe", new page_ejecting_reg);