Fix computation of .trunc register. Additionally, its value (and
[s-roff.git] / src / roff / troff / div.cc
blob52e99e9aa1bae2eb70a138a0aa8d67f38209e04d
1 // -*- C++ -*-
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
11 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
16 for more details.
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. */
23 // diversions
25 #include "troff.h"
26 #include "symbol.h"
27 #include "dictionary.h"
28 #include "hvunits.h"
29 #include "env.h"
30 #include "request.h"
31 #include "node.h"
32 #include "token.h"
33 #include "div.h"
34 #include "reg.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
40 // specified with -o
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)
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, int boxing)
95 tok.skip();
96 symbol nm = get_name();
97 if (nm.is_null()) {
98 if (curdiv->prev) {
99 if (boxing) {
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;
109 delete temp;
111 else
112 warning(WARN_DI, "diversion stack underflow");
114 else {
115 macro_diversion *md = new macro_diversion(nm, append);
116 md->prev = curdiv;
117 curdiv = md;
118 if (boxing) {
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;
125 curenv->line = 0;
126 curenv->start_line();
129 skip_line();
132 void divert()
134 do_divert(0, 0);
137 void divert_append()
139 do_divert(1, 0);
142 void box()
144 do_divert(0, 1);
147 void box_append()
149 do_divert(1, 1);
152 void diversion::need(vunits n)
154 vunits d = distance_to_next_trap();
155 if (d < n) {
156 truncated_space = -d;
157 needed_space = n;
158 space(d, 1);
162 macro_diversion::macro_diversion(symbol s, int append)
163 : diversion(s), max_width(H0)
165 #if 0
166 if (append) {
167 /* We don't allow recursive appends eg:
169 .da a
173 This causes an infinite loop in troff anyway.
174 This is because the user could do
176 .as a foo
178 in the diversion, and this would mess things up royally,
179 since there would be two things appending to the same
180 macro_header.
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.
184 However,
186 .di a
191 will work and will make `a' contain two copies of what it contained
192 before; in troff, `a' would contain nothing. */
193 request_or_macro *rm
194 = (request_or_macro *)request_dictionary.remove(s);
195 if (!rm || (mac = rm->to_macro()) == 0)
196 mac = new macro;
198 else
199 mac = new macro;
200 #endif
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.
204 mac = new macro;
205 if (append) {
206 request_or_macro *rm
207 = (request_or_macro *)request_dictionary.lookup(s);
208 if (rm) {
209 macro *m = rm->to_macro();
210 if (m)
211 *mac = *m;
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;
220 if (m) {
221 *m = *mac;
222 delete mac;
224 else
225 request_dictionary.define(nm, mac);
226 mac = 0;
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;
235 else
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)
242 mac->append(c);
245 void macro_diversion::transparent_output(node *n)
247 mac->append(n);
250 void macro_diversion::output(node *nd, int retain_size,
251 vunits vs, vunits post_vs, hunits width)
253 no_space_mode = 0;
254 vertical_size v(vs, post_vs);
255 while (nd != 0) {
256 nd->set_vertical_size(&v);
257 node *temp = nd;
258 nd = nd->next;
259 if (temp->interpret(mac)) {
260 delete temp;
262 else {
263 #if 1
264 temp->freeze_space();
265 #endif
266 mac->append(temp);
269 last_post_line_extra_space = v.post_extra.to_units();
270 if (!retain_size) {
271 v.pre = vs;
272 v.post = post_vs;
274 if (width > max_width)
275 max_width = 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;
281 if (trunc > v.post)
282 trunc = v.post;
283 v.post -= trunc;
284 x -= trunc;
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));
290 mac->append('\n');
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)
329 trap *next_trap = 0;
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)) {
336 next_trap = pt;
337 *next_trap_pos = pt->position;
340 else {
341 vunits pos = pt->position;
342 pos += page_length;
343 if (pos > 0 && pos > vertical_position && (next_trap == 0 || pos < *next_trap_pos)) {
344 next_trap = pt;
345 *next_trap_pos = pos;
349 return next_trap;
352 vunits top_level_diversion::distance_to_next_trap()
354 vunits d;
355 if (!find_next_trap(&d))
356 return page_length - vertical_position;
357 else
358 return d - vertical_position;
361 void top_level_diversion::output(node *nd, int retain_size,
362 vunits vs, vunits post_vs, hunits width)
364 no_space_mode = 0;
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();
373 if (!retain_size) {
374 v.pre = vs;
375 v.post = post_vs;
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)
385 begin_page();
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)
402 begin_page();
403 else
404 nl_reg_contents = vertical_position.to_units();
406 else
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);
416 while (*s)
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)
434 if (no_space_mode) {
435 if (!forced)
436 return;
437 else
438 no_space_mode = 0;
440 if (before_first_page) {
441 begin_page(n);
442 return;
444 vunits next_trap_pos;
445 trap *next_trap = find_next_trap(&next_trap_pos);
446 vunits y = vertical_position + n;
447 if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) {
448 vertical_position = next_trap_pos;
449 nl_reg_contents = vertical_position.to_units();
450 truncated_space = y - vertical_position;
451 spring_trap(next_trap->nm);
453 else if (y < V0) {
454 vertical_position = V0;
455 nl_reg_contents = vertical_position.to_units();
457 else if (vertical_position_traps_flag && y >= page_length && n >= V0)
458 begin_page(y - page_length);
459 else {
460 vertical_position = y;
461 nl_reg_contents = vertical_position.to_units();
465 trap::trap(symbol s, vunits n, trap *p)
466 : next(p), position(n), nm(s)
470 void top_level_diversion::add_trap(symbol nm, vunits pos)
472 trap *first_free_slot = 0;
473 trap **p;
474 for (p = &page_trap_list; *p; p = &(*p)->next) {
475 if ((*p)->nm.is_null()) {
476 if (first_free_slot == 0)
477 first_free_slot = *p;
479 else if ((*p)->position == pos) {
480 (*p)->nm = nm;
481 return;
484 if (first_free_slot) {
485 first_free_slot->nm = nm;
486 first_free_slot->position = pos;
488 else
489 *p = new trap(nm, pos, 0);
492 void top_level_diversion::remove_trap(symbol nm)
494 for (trap *p = page_trap_list; p; p = p->next)
495 if (p->nm == nm) {
496 p->nm = NULL_SYMBOL;
497 return;
501 void top_level_diversion::remove_trap_at(vunits pos)
503 for (trap *p = page_trap_list; p; p = p->next)
504 if (p->position == pos) {
505 p->nm = NULL_SYMBOL;
506 return;
510 void top_level_diversion::change_trap(symbol nm, vunits pos)
512 for (trap *p = page_trap_list; p; p = p->next)
513 if (p->nm == nm) {
514 p->position = pos;
515 return;
519 void top_level_diversion::print_traps()
521 for (trap *p = page_trap_list; p; p = p->next)
522 if (p->nm.is_null())
523 fprintf(stderr, " empty\n");
524 else
525 fprintf(stderr, "%s\t%d\n", p->nm.contents(), p->position.to_units());
526 fflush(stderr);
529 void end_diversions()
531 while (curdiv != topdiv) {
532 error("automatically ending diversion `%1' on exit",
533 curdiv->nm.contents());
534 diversion *tem = curdiv;
535 curdiv = curdiv->prev;
536 delete tem;
540 void cleanup_and_exit(int exit_code)
542 if (the_output) {
543 the_output->trailer(topdiv->get_page_length());
544 delete the_output;
546 exit(exit_code);
549 // Returns non-zero if it sprung a top-of-page trap.
550 // The optional parameter is for the .trunc register.
551 int top_level_diversion::begin_page(vunits n)
553 if (exit_started) {
554 if (page_count == last_page_count
555 ? curenv->is_empty()
556 : (done_end_macro && (seen_last_page_ejector || began_page_in_end_macro)))
557 cleanup_and_exit(0);
558 if (!done_end_macro)
559 began_page_in_end_macro = 1;
561 if (last_page_number > 0 && page_number == last_page_number)
562 cleanup_and_exit(0);
563 if (!the_output)
564 init_output();
565 ++page_count;
566 if (have_next_page_number) {
567 page_number = next_page_number;
568 have_next_page_number = 0;
570 else if (before_first_page == 1)
571 page_number = 1;
572 else
573 page_number++;
574 // spring the top of page trap if there is one
575 vunits next_trap_pos;
576 vertical_position = -vresolution;
577 trap *next_trap = find_next_trap(&next_trap_pos);
578 vertical_position = V0;
579 high_water_mark = V0;
580 ejecting_page = 0;
581 // If before_first_page was 2, then the top of page transition was undone
582 // using eg .nr nl 0-1. See nl_reg::set_value.
583 if (before_first_page != 2)
584 the_output->begin_page(page_number, page_length);
585 before_first_page = 0;
586 nl_reg_contents = vertical_position.to_units();
587 if (vertical_position_traps_flag && next_trap != 0 && next_trap_pos == V0) {
588 truncated_space = n;
589 spring_trap(next_trap->nm);
590 return 1;
592 else
593 return 0;
596 void continue_page_eject()
598 if (topdiv->get_ejecting()) {
599 if (curdiv != topdiv)
600 error("can't continue page ejection because of current diversion");
601 else if (!vertical_position_traps_flag)
602 error("can't continue page ejection because vertical position traps disabled");
603 else {
604 push_page_ejector();
605 topdiv->space(topdiv->get_page_length(), 1);
610 void top_level_diversion::set_next_page_number(int n)
612 next_page_number= n;
613 have_next_page_number = 1;
616 int top_level_diversion::get_next_page_number()
618 return have_next_page_number ? next_page_number : page_number + 1;
621 void top_level_diversion::set_page_length(vunits n)
623 page_length = n;
626 diversion::~diversion()
630 void page_offset()
632 hunits n;
633 // The troff manual says that the default scaling indicator is v,
634 // but it is in fact m: v wouldn't make sense for a horizontally
635 // oriented request.
636 if (!has_arg() || !get_hunits(&n, 'm', topdiv->page_offset))
637 n = topdiv->prev_page_offset;
638 topdiv->prev_page_offset = topdiv->page_offset;
639 topdiv->page_offset = n;
640 curenv->add_html_tag(0, ".po", n.to_units());
641 skip_line();
644 void page_length()
646 vunits n;
647 if (has_arg() && get_vunits(&n, 'v', topdiv->get_page_length()))
648 topdiv->set_page_length(n);
649 else
650 topdiv->set_page_length(11*units_per_inch);
651 skip_line();
654 void when_request()
656 vunits n;
657 if (get_vunits(&n, 'v')) {
658 symbol s = get_name();
659 if (s.is_null())
660 topdiv->remove_trap_at(n);
661 else
662 topdiv->add_trap(s, n);
664 skip_line();
667 void begin_page()
669 int got_arg = 0;
670 int n;
671 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
672 got_arg = 1;
673 while (!tok.newline() && !tok.eof())
674 tok.next();
675 if (curdiv == topdiv) {
676 if (topdiv->before_first_page) {
677 if (!break_flag) {
678 if (got_arg)
679 topdiv->set_next_page_number(n);
680 if (got_arg || !topdiv->no_space_mode)
681 topdiv->begin_page();
683 else if (topdiv->no_space_mode && !got_arg)
684 topdiv->begin_page();
685 else {
686 /* Given this
688 .wh 0 x
689 .de x
690 .tm \\n%
692 .bp 3
694 troff prints
699 This code makes groff do the same. */
701 push_page_ejector();
702 topdiv->begin_page();
703 if (got_arg)
704 topdiv->set_next_page_number(n);
705 topdiv->set_ejecting();
708 else {
709 push_page_ejector();
710 if (break_flag)
711 curenv->do_break();
712 if (got_arg)
713 topdiv->set_next_page_number(n);
714 if (!(topdiv->no_space_mode && !got_arg))
715 topdiv->set_ejecting();
718 tok.next();
721 void no_space()
723 curdiv->no_space_mode = 1;
724 skip_line();
727 void restore_spacing()
729 curdiv->no_space_mode = 0;
730 skip_line();
733 /* It is necessary to generate a break before before reading the argument,
734 because otherwise arguments using | will be wrong. But if we just
735 generate a break as usual, then the line forced out may spring a trap
736 and thus push a macro onto the input stack before we have had a chance
737 to read the argument to the sp request. We resolve this dilemma by
738 setting, before generating the break, a flag which will postpone the
739 actual pushing of the macro associated with the trap sprung by the
740 outputting of the line forced out by the break till after we have read
741 the argument to the request. If the break did cause a trap to be
742 sprung, then we don't actually do the space. */
744 void space_request()
746 postpone_traps();
747 if (break_flag)
748 curenv->do_break();
749 vunits n;
750 if (!has_arg() || !get_vunits(&n, 'v'))
751 n = curenv->get_vertical_spacing();
752 while (!tok.newline() && !tok.eof())
753 tok.next();
754 if (!unpostpone_traps() && !curdiv->no_space_mode)
755 curdiv->space(n);
756 else
757 // The line might have had line spacing that was truncated.
758 truncated_space += n;
759 curenv->add_html_tag(1, ".sp", n.to_units());
760 tok.next();
763 void blank_line()
765 curenv->do_break();
766 if (!trap_sprung_flag && !curdiv->no_space_mode) {
767 curdiv->space(curenv->get_vertical_spacing());
768 curenv->add_html_tag(1, ".sp", 1);
770 else
771 truncated_space += curenv->get_vertical_spacing();
774 /* need_space might spring a trap and so we must be careful that the
775 BEGIN_TRAP token is not skipped over. */
777 void need_space()
779 vunits n;
780 if (!has_arg() || !get_vunits(&n, 'v'))
781 n = curenv->get_vertical_spacing();
782 while (!tok.newline() && !tok.eof())
783 tok.next();
784 curdiv->need(n);
785 tok.next();
788 void page_number()
790 int n;
792 // the ps4html register is set if we are using -Tps
793 // to generate images for html
794 reg *r = (reg *)number_reg_dictionary.lookup("ps4html");
795 if (r == NULL)
796 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
797 topdiv->set_next_page_number(n);
798 skip_line();
801 vunits saved_space;
803 void save_vertical_space()
805 vunits x;
806 if (!has_arg() || !get_vunits(&x, 'v'))
807 x = curenv->get_vertical_spacing();
808 if (curdiv->distance_to_next_trap() > x)
809 curdiv->space(x, 1);
810 else
811 saved_space = x;
812 skip_line();
815 void output_saved_vertical_space()
817 while (!tok.newline() && !tok.eof())
818 tok.next();
819 if (saved_space > V0)
820 curdiv->space(saved_space, 1);
821 saved_space = V0;
822 tok.next();
825 void flush_output()
827 while (!tok.newline() && !tok.eof())
828 tok.next();
829 if (break_flag)
830 curenv->do_break();
831 if (the_output)
832 the_output->flush();
833 curenv->add_html_tag(1, ".fl");
834 tok.next();
837 void macro_diversion::set_diversion_trap(symbol s, vunits n)
839 diversion_trap = s;
840 diversion_trap_pos = n;
843 void macro_diversion::clear_diversion_trap()
845 diversion_trap = NULL_SYMBOL;
848 void top_level_diversion::set_diversion_trap(symbol, vunits)
850 error("can't set diversion trap when no current diversion");
853 void top_level_diversion::clear_diversion_trap()
855 error("can't set diversion trap when no current diversion");
858 void diversion_trap()
860 vunits n;
861 if (has_arg() && get_vunits(&n, 'v')) {
862 symbol s = get_name();
863 if (!s.is_null())
864 curdiv->set_diversion_trap(s, n);
865 else
866 curdiv->clear_diversion_trap();
868 else
869 curdiv->clear_diversion_trap();
870 skip_line();
873 void change_trap()
875 symbol s = get_name(1);
876 if (!s.is_null()) {
877 vunits x;
878 if (has_arg() && get_vunits(&x, 'v'))
879 topdiv->change_trap(s, x);
880 else
881 topdiv->remove_trap(s);
883 skip_line();
886 void print_traps()
888 topdiv->print_traps();
889 skip_line();
892 void mark()
894 symbol s = get_name();
895 if (s.is_null())
896 curdiv->marked_place = curdiv->get_vertical_position();
897 else if (curdiv == topdiv)
898 set_number_reg(s, nl_reg_contents);
899 else
900 set_number_reg(s, curdiv->get_vertical_position().to_units());
901 skip_line();
904 // This is truly bizarre. It is documented in the SQ manual.
906 void return_request()
908 vunits dist = curdiv->marked_place - curdiv->get_vertical_position();
909 if (has_arg()) {
910 if (tok.ch() == '-') {
911 tok.next();
912 vunits x;
913 if (get_vunits(&x, 'v'))
914 dist = -x;
916 else {
917 vunits x;
918 if (get_vunits(&x, 'v'))
919 dist = x >= V0 ? x - curdiv->get_vertical_position() : V0;
922 if (dist < V0)
923 curdiv->space(dist);
924 skip_line();
927 void vertical_position_traps()
929 int n;
930 if (has_arg() && get_integer(&n))
931 vertical_position_traps_flag = (n != 0);
932 else
933 vertical_position_traps_flag = 1;
934 skip_line();
937 class page_offset_reg : public reg {
938 public:
939 int get_value(units *);
940 const char *get_string();
943 int page_offset_reg::get_value(units *res)
945 *res = topdiv->get_page_offset().to_units();
946 return 1;
949 const char *page_offset_reg::get_string()
951 return i_to_a(topdiv->get_page_offset().to_units());
954 class page_length_reg : public reg {
955 public:
956 int get_value(units *);
957 const char *get_string();
960 int page_length_reg::get_value(units *res)
962 *res = topdiv->get_page_length().to_units();
963 return 1;
966 const char *page_length_reg::get_string()
968 return i_to_a(topdiv->get_page_length().to_units());
971 class vertical_position_reg : public reg {
972 public:
973 int get_value(units *);
974 const char *get_string();
977 int vertical_position_reg::get_value(units *res)
979 if (curdiv == topdiv && topdiv->before_first_page)
980 *res = -1;
981 else
982 *res = curdiv->get_vertical_position().to_units();
983 return 1;
986 const char *vertical_position_reg::get_string()
988 if (curdiv == topdiv && topdiv->before_first_page)
989 return "-1";
990 else
991 return i_to_a(curdiv->get_vertical_position().to_units());
994 class high_water_mark_reg : public reg {
995 public:
996 int get_value(units *);
997 const char *get_string();
1000 int high_water_mark_reg::get_value(units *res)
1002 *res = curdiv->get_high_water_mark().to_units();
1003 return 1;
1006 const char *high_water_mark_reg::get_string()
1008 return i_to_a(curdiv->get_high_water_mark().to_units());
1011 class distance_to_next_trap_reg : public reg {
1012 public:
1013 int get_value(units *);
1014 const char *get_string();
1017 int distance_to_next_trap_reg::get_value(units *res)
1019 *res = curdiv->distance_to_next_trap().to_units();
1020 return 1;
1023 const char *distance_to_next_trap_reg::get_string()
1025 return i_to_a(curdiv->distance_to_next_trap().to_units());
1028 class diversion_name_reg : public reg {
1029 public:
1030 const char *get_string();
1033 const char *diversion_name_reg::get_string()
1035 return curdiv->get_diversion_name();
1038 class page_number_reg : public general_reg {
1039 public:
1040 page_number_reg();
1041 int get_value(units *);
1042 void set_value(units);
1045 page_number_reg::page_number_reg()
1049 void page_number_reg::set_value(units n)
1051 topdiv->set_page_number(n);
1054 int page_number_reg::get_value(units *res)
1056 *res = topdiv->get_page_number();
1057 return 1;
1060 class next_page_number_reg : public reg {
1061 public:
1062 const char *get_string();
1065 const char *next_page_number_reg::get_string()
1067 return i_to_a(topdiv->get_next_page_number());
1070 class page_ejecting_reg : public reg {
1071 public:
1072 const char *get_string();
1075 const char *page_ejecting_reg::get_string()
1077 return i_to_a(topdiv->get_ejecting());
1080 class constant_vunits_reg : public reg {
1081 vunits *p;
1082 public:
1083 constant_vunits_reg(vunits *);
1084 const char *get_string();
1087 constant_vunits_reg::constant_vunits_reg(vunits *q) : p(q)
1091 const char *constant_vunits_reg::get_string()
1093 return i_to_a(p->to_units());
1096 class nl_reg : public variable_reg {
1097 public:
1098 nl_reg();
1099 void set_value(units);
1102 nl_reg::nl_reg() : variable_reg(&nl_reg_contents)
1106 void nl_reg::set_value(units n)
1108 variable_reg::set_value(n);
1109 // Setting nl to a negative value when the vertical position in
1110 // the top-level diversion is 0 undoes the top of page transition,
1111 // so that the header macro will be called as if the top of page
1112 // transition hasn't happened. This is used by Larry Wall's
1113 // wrapman program. Setting before_first_page to 2 rather than 1,
1114 // tells top_level_diversion::begin_page not to call
1115 // output_file::begin_page again.
1116 if (n < 0 && topdiv->get_vertical_position() == V0)
1117 topdiv->before_first_page = 2;
1120 class no_space_mode_reg : public reg {
1121 public:
1122 int get_value(units *);
1123 const char *get_string();
1126 int no_space_mode_reg::get_value(units *val)
1128 *val = curdiv->no_space_mode;
1129 return 1;
1132 const char *no_space_mode_reg::get_string()
1134 return curdiv->no_space_mode ? "1" : "0";
1137 void init_div_requests()
1139 init_request("wh", when_request);
1140 init_request("ch", change_trap);
1141 init_request("pl", page_length);
1142 init_request("po", page_offset);
1143 init_request("rs", restore_spacing);
1144 init_request("ns", no_space);
1145 init_request("sp", space_request);
1146 init_request("di", divert);
1147 init_request("da", divert_append);
1148 init_request("box", box);
1149 init_request("boxa", box_append);
1150 init_request("bp", begin_page);
1151 init_request("ne", need_space);
1152 init_request("pn", page_number);
1153 init_request("dt", diversion_trap);
1154 init_request("rt", return_request);
1155 init_request("mk", mark);
1156 init_request("sv", save_vertical_space);
1157 init_request("os", output_saved_vertical_space);
1158 init_request("fl", flush_output);
1159 init_request("vpt", vertical_position_traps);
1160 init_request("ptr", print_traps);
1161 number_reg_dictionary.define(".a",
1162 new constant_int_reg(&last_post_line_extra_space));
1163 number_reg_dictionary.define(".z", new diversion_name_reg);
1164 number_reg_dictionary.define(".o", new page_offset_reg);
1165 number_reg_dictionary.define(".p", new page_length_reg);
1166 number_reg_dictionary.define(".ns", new no_space_mode_reg);
1167 number_reg_dictionary.define(".d", new vertical_position_reg);
1168 number_reg_dictionary.define(".h", new high_water_mark_reg);
1169 number_reg_dictionary.define(".t", new distance_to_next_trap_reg);
1170 number_reg_dictionary.define("dl", new variable_reg(&dl_reg_contents));
1171 number_reg_dictionary.define("dn", new variable_reg(&dn_reg_contents));
1172 number_reg_dictionary.define("nl", new nl_reg);
1173 number_reg_dictionary.define(".vpt",
1174 new constant_int_reg(&vertical_position_traps_flag));
1175 number_reg_dictionary.define("%", new page_number_reg);
1176 number_reg_dictionary.define(".pn", new next_page_number_reg);
1177 number_reg_dictionary.define(".trunc",
1178 new constant_vunits_reg(&truncated_space));
1179 number_reg_dictionary.define(".ne",
1180 new constant_vunits_reg(&needed_space));
1181 number_reg_dictionary.define(".pe", new page_ejecting_reg);