* Makefile.in (SEP): Replaced with...
[s-roff.git] / src / roff / troff / div.cpp
blob084d0b031ba44ffcc825ffd3c1137e6dc7aed50a
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2004
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 "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 #include "nonposix.h"
37 int exit_started = 0; // the exit process has started
38 int done_end_macro = 0; // the end macro (if any) has finished
39 int seen_last_page_ejector = 0; // seen the LAST_PAGE_EJECTOR cookie
40 int last_page_number = 0; // if > 0, the number of the last page
41 // specified with -o
42 static int began_page_in_end_macro = 0; // a new page was begun during the end macro
44 static int last_post_line_extra_space = 0; // needed for \n(.a
45 static int nl_reg_contents = -1;
46 static int dl_reg_contents = 0;
47 static int dn_reg_contents = 0;
48 static int vertical_position_traps_flag = 1;
49 static vunits truncated_space;
50 static vunits needed_space;
52 diversion::diversion(symbol s)
53 : prev(0), nm(s), vertical_position(V0), high_water_mark(V0),
54 no_space_mode(0), marked_place(V0)
58 struct vertical_size {
59 vunits pre_extra, post_extra, pre, post;
60 vertical_size(vunits vs, vunits post_vs);
63 vertical_size::vertical_size(vunits vs, vunits post_vs)
64 : pre_extra(V0), post_extra(V0), pre(vs), post(post_vs)
68 void node::set_vertical_size(vertical_size *)
72 void extra_size_node::set_vertical_size(vertical_size *v)
74 if (n < V0) {
75 if (-n > v->pre_extra)
76 v->pre_extra = -n;
78 else if (n > v->post_extra)
79 v->post_extra = n;
82 void vertical_size_node::set_vertical_size(vertical_size *v)
84 if (n < V0)
85 v->pre = -n;
86 else
87 v->post = n;
90 top_level_diversion *topdiv;
92 diversion *curdiv;
94 void do_divert(int append, int boxing)
96 tok.skip();
97 symbol nm = get_name();
98 if (nm.is_null()) {
99 if (curdiv->prev) {
100 if (boxing) {
101 curenv->line = curdiv->saved_line;
102 curenv->width_total = curdiv->saved_width_total;
103 curenv->space_total = curdiv->saved_space_total;
104 curenv->saved_indent = curdiv->saved_saved_indent;
105 curenv->target_text_length = curdiv->saved_target_text_length;
106 curenv->prev_line_interrupted = curdiv->saved_prev_line_interrupted;
108 diversion *temp = curdiv;
109 curdiv = curdiv->prev;
110 delete temp;
112 else
113 warning(WARN_DI, "diversion stack underflow");
115 else {
116 macro_diversion *md = new macro_diversion(nm, append);
117 md->prev = curdiv;
118 curdiv = md;
119 if (boxing) {
120 curdiv->saved_line = curenv->line;
121 curdiv->saved_width_total = curenv->width_total;
122 curdiv->saved_space_total = curenv->space_total;
123 curdiv->saved_saved_indent = curenv->saved_indent;
124 curdiv->saved_target_text_length = curenv->target_text_length;
125 curdiv->saved_prev_line_interrupted = curenv->prev_line_interrupted;
126 curenv->line = 0;
127 curenv->start_line();
130 skip_line();
133 void divert()
135 do_divert(0, 0);
138 void divert_append()
140 do_divert(1, 0);
143 void box()
145 do_divert(0, 1);
148 void box_append()
150 do_divert(1, 1);
153 void diversion::need(vunits n)
155 vunits d = distance_to_next_trap();
156 if (d < n) {
157 truncated_space = -d;
158 needed_space = n;
159 space(d, 1);
163 macro_diversion::macro_diversion(symbol s, int append)
164 : diversion(s), max_width(H0)
166 #if 0
167 if (append) {
168 /* We don't allow recursive appends eg:
170 .da a
174 This causes an infinite loop in troff anyway.
175 This is because the user could do
177 .as a foo
179 in the diversion, and this would mess things up royally,
180 since there would be two things appending to the same
181 macro_header.
182 To make it work, we would have to copy the _contents_
183 of the macro into which we were diverting; this doesn't
184 strike me as worthwhile.
185 However,
187 .di a
192 will work and will make `a' contain two copies of what it contained
193 before; in troff, `a' would contain nothing. */
194 request_or_macro *rm
195 = (request_or_macro *)request_dictionary.remove(s);
196 if (!rm || (mac = rm->to_macro()) == 0)
197 mac = new macro;
199 else
200 mac = new macro;
201 #endif
202 // We can now catch the situation described above by comparing
203 // the length of the charlist in the macro_header with the length
204 // stored in the macro. When we detect this, we copy the contents.
205 mac = new macro;
206 if (append) {
207 request_or_macro *rm
208 = (request_or_macro *)request_dictionary.lookup(s);
209 if (rm) {
210 macro *m = rm->to_macro();
211 if (m)
212 *mac = *m;
217 macro_diversion::~macro_diversion()
219 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
220 macro *m = rm ? rm->to_macro() : 0;
221 if (m) {
222 *m = *mac;
223 delete mac;
225 else
226 request_dictionary.define(nm, mac);
227 mac = 0;
228 dl_reg_contents = max_width.to_units();
229 dn_reg_contents = vertical_position.to_units();
232 vunits macro_diversion::distance_to_next_trap()
234 if (!diversion_trap.is_null() && diversion_trap_pos > vertical_position)
235 return diversion_trap_pos - vertical_position;
236 else
237 // Substract vresolution so that vunits::vunits does not overflow.
238 return vunits(INT_MAX - vresolution);
241 void macro_diversion::transparent_output(unsigned char c)
243 mac->append(c);
246 void macro_diversion::transparent_output(node *n)
248 mac->append(n);
251 void macro_diversion::output(node *nd, int retain_size,
252 vunits vs, vunits post_vs, hunits width)
254 no_space_mode = 0;
255 vertical_size v(vs, post_vs);
256 while (nd != 0) {
257 nd->set_vertical_size(&v);
258 node *temp = nd;
259 nd = nd->next;
260 if (temp->interpret(mac)) {
261 delete temp;
263 else {
264 #if 1
265 temp->freeze_space();
266 #endif
267 mac->append(temp);
270 last_post_line_extra_space = v.post_extra.to_units();
271 if (!retain_size) {
272 v.pre = vs;
273 v.post = post_vs;
275 if (width > max_width)
276 max_width = width;
277 vunits x = v.pre + v.pre_extra + v.post + v.post_extra;
278 if (vertical_position_traps_flag
279 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
280 && diversion_trap_pos <= vertical_position + x) {
281 vunits trunc = vertical_position + x - diversion_trap_pos;
282 if (trunc > v.post)
283 trunc = v.post;
284 v.post -= trunc;
285 x -= trunc;
286 truncated_space = trunc;
287 spring_trap(diversion_trap);
289 mac->append(new vertical_size_node(-v.pre));
290 mac->append(new vertical_size_node(v.post));
291 mac->append('\n');
292 vertical_position += x;
293 if (vertical_position - v.post > high_water_mark)
294 high_water_mark = vertical_position - v.post;
297 void macro_diversion::space(vunits n, int)
299 if (vertical_position_traps_flag
300 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
301 && diversion_trap_pos <= vertical_position + n) {
302 truncated_space = vertical_position + n - diversion_trap_pos;
303 n = diversion_trap_pos - vertical_position;
304 spring_trap(diversion_trap);
306 else if (n + vertical_position < V0)
307 n = -vertical_position;
308 mac->append(new diverted_space_node(n));
309 vertical_position += n;
312 void macro_diversion::copy_file(const char *filename)
314 mac->append(new diverted_copy_file_node(filename));
317 top_level_diversion::top_level_diversion()
318 : page_number(0), page_count(0), last_page_count(-1),
319 page_length(units_per_inch*11),
320 prev_page_offset(units_per_inch), page_offset(units_per_inch),
321 page_trap_list(0), have_next_page_number(0),
322 ejecting_page(0), before_first_page(1)
326 // find the next trap after pos
328 trap *top_level_diversion::find_next_trap(vunits *next_trap_pos)
330 trap *next_trap = 0;
331 for (trap *pt = page_trap_list; pt != 0; pt = pt->next)
332 if (!pt->nm.is_null()) {
333 if (pt->position >= V0) {
334 if (pt->position > vertical_position
335 && pt->position < page_length
336 && (next_trap == 0 || pt->position < *next_trap_pos)) {
337 next_trap = pt;
338 *next_trap_pos = pt->position;
341 else {
342 vunits pos = pt->position;
343 pos += page_length;
344 if (pos > 0 && pos > vertical_position && (next_trap == 0 || pos < *next_trap_pos)) {
345 next_trap = pt;
346 *next_trap_pos = pos;
350 return next_trap;
353 vunits top_level_diversion::distance_to_next_trap()
355 vunits d;
356 if (!find_next_trap(&d))
357 return page_length - vertical_position;
358 else
359 return d - vertical_position;
362 void top_level_diversion::output(node *nd, int retain_size,
363 vunits vs, vunits post_vs, hunits width)
365 no_space_mode = 0;
366 vunits next_trap_pos;
367 trap *next_trap = find_next_trap(&next_trap_pos);
368 if (before_first_page && begin_page())
369 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
370 vertical_size v(vs, post_vs);
371 for (node *tem = nd; tem != 0; tem = tem->next)
372 tem->set_vertical_size(&v);
373 last_post_line_extra_space = v.post_extra.to_units();
374 if (!retain_size) {
375 v.pre = vs;
376 v.post = post_vs;
378 vertical_position += v.pre;
379 vertical_position += v.pre_extra;
380 the_output->print_line(page_offset, vertical_position, nd,
381 v.pre + v.pre_extra, v.post_extra, width);
382 vertical_position += v.post_extra;
383 if (vertical_position > high_water_mark)
384 high_water_mark = vertical_position;
385 if (vertical_position_traps_flag && vertical_position >= page_length)
386 begin_page();
387 else if (vertical_position_traps_flag
388 && next_trap != 0 && vertical_position >= next_trap_pos) {
389 nl_reg_contents = vertical_position.to_units();
390 truncated_space = v.post;
391 spring_trap(next_trap->nm);
393 else if (v.post > V0) {
394 vertical_position += v.post;
395 if (vertical_position_traps_flag
396 && next_trap != 0 && vertical_position >= next_trap_pos) {
397 truncated_space = vertical_position - next_trap_pos;
398 vertical_position = next_trap_pos;
399 nl_reg_contents = vertical_position.to_units();
400 spring_trap(next_trap->nm);
402 else if (vertical_position_traps_flag && vertical_position >= page_length)
403 begin_page();
404 else
405 nl_reg_contents = vertical_position.to_units();
407 else
408 nl_reg_contents = vertical_position.to_units();
411 void top_level_diversion::transparent_output(unsigned char c)
413 if (before_first_page && begin_page())
414 // This can only happen with the .output request.
415 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
416 const char *s = asciify(c);
417 while (*s)
418 the_output->transparent_char(*s++);
421 void top_level_diversion::transparent_output(node * /*n*/)
423 error("can't transparently output node at top level");
426 void top_level_diversion::copy_file(const char *filename)
428 if (before_first_page && begin_page())
429 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
430 the_output->copy_file(page_offset, vertical_position, filename);
433 void top_level_diversion::space(vunits n, int forced)
435 if (no_space_mode) {
436 if (!forced)
437 return;
438 else
439 no_space_mode = 0;
441 if (before_first_page) {
442 begin_page(n);
443 return;
445 vunits next_trap_pos;
446 trap *next_trap = find_next_trap(&next_trap_pos);
447 vunits y = vertical_position + n;
448 if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) {
449 vertical_position = next_trap_pos;
450 nl_reg_contents = vertical_position.to_units();
451 truncated_space = y - vertical_position;
452 spring_trap(next_trap->nm);
454 else if (y < V0) {
455 vertical_position = V0;
456 nl_reg_contents = vertical_position.to_units();
458 else if (vertical_position_traps_flag && y >= page_length && n >= V0)
459 begin_page(y - page_length);
460 else {
461 vertical_position = y;
462 nl_reg_contents = vertical_position.to_units();
466 trap::trap(symbol s, vunits n, trap *p)
467 : next(p), position(n), nm(s)
471 void top_level_diversion::add_trap(symbol nm, vunits pos)
473 trap *first_free_slot = 0;
474 trap **p;
475 for (p = &page_trap_list; *p; p = &(*p)->next) {
476 if ((*p)->nm.is_null()) {
477 if (first_free_slot == 0)
478 first_free_slot = *p;
480 else if ((*p)->position == pos) {
481 (*p)->nm = nm;
482 return;
485 if (first_free_slot) {
486 first_free_slot->nm = nm;
487 first_free_slot->position = pos;
489 else
490 *p = new trap(nm, pos, 0);
493 void top_level_diversion::remove_trap(symbol nm)
495 for (trap *p = page_trap_list; p; p = p->next)
496 if (p->nm == nm) {
497 p->nm = NULL_SYMBOL;
498 return;
502 void top_level_diversion::remove_trap_at(vunits pos)
504 for (trap *p = page_trap_list; p; p = p->next)
505 if (p->position == pos) {
506 p->nm = NULL_SYMBOL;
507 return;
511 void top_level_diversion::change_trap(symbol nm, vunits pos)
513 for (trap *p = page_trap_list; p; p = p->next)
514 if (p->nm == nm) {
515 p->position = pos;
516 return;
520 void top_level_diversion::print_traps()
522 for (trap *p = page_trap_list; p; p = p->next)
523 if (p->nm.is_null())
524 fprintf(stderr, " empty\n");
525 else
526 fprintf(stderr, "%s\t%d\n", p->nm.contents(), p->position.to_units());
527 fflush(stderr);
530 void end_diversions()
532 while (curdiv != topdiv) {
533 error("automatically ending diversion `%1' on exit",
534 curdiv->nm.contents());
535 diversion *tem = curdiv;
536 curdiv = curdiv->prev;
537 delete tem;
541 void cleanup_and_exit(int exit_code)
543 if (the_output) {
544 the_output->trailer(topdiv->get_page_length());
545 delete the_output;
547 FLUSH_INPUT_PIPE(STDIN_FILENO);
548 exit(exit_code);
551 // Returns non-zero if it sprung a top-of-page trap.
552 // The optional parameter is for the .trunc register.
553 int top_level_diversion::begin_page(vunits n)
555 if (exit_started) {
556 if (page_count == last_page_count
557 ? curenv->is_empty()
558 : (done_end_macro && (seen_last_page_ejector || began_page_in_end_macro)))
559 cleanup_and_exit(0);
560 if (!done_end_macro)
561 began_page_in_end_macro = 1;
563 if (last_page_number > 0 && page_number == last_page_number)
564 cleanup_and_exit(0);
565 if (!the_output)
566 init_output();
567 ++page_count;
568 if (have_next_page_number) {
569 page_number = next_page_number;
570 have_next_page_number = 0;
572 else if (before_first_page == 1)
573 page_number = 1;
574 else
575 page_number++;
576 // spring the top of page trap if there is one
577 vunits next_trap_pos;
578 vertical_position = -vresolution;
579 trap *next_trap = find_next_trap(&next_trap_pos);
580 vertical_position = V0;
581 high_water_mark = V0;
582 ejecting_page = 0;
583 // If before_first_page was 2, then the top of page transition was undone
584 // using eg .nr nl 0-1. See nl_reg::set_value.
585 if (before_first_page != 2)
586 the_output->begin_page(page_number, page_length);
587 before_first_page = 0;
588 nl_reg_contents = vertical_position.to_units();
589 if (vertical_position_traps_flag && next_trap != 0 && next_trap_pos == V0) {
590 truncated_space = n;
591 spring_trap(next_trap->nm);
592 return 1;
594 else
595 return 0;
598 void continue_page_eject()
600 if (topdiv->get_ejecting()) {
601 if (curdiv != topdiv)
602 error("can't continue page ejection because of current diversion");
603 else if (!vertical_position_traps_flag)
604 error("can't continue page ejection because vertical position traps disabled");
605 else {
606 push_page_ejector();
607 topdiv->space(topdiv->get_page_length(), 1);
612 void top_level_diversion::set_next_page_number(int n)
614 next_page_number= n;
615 have_next_page_number = 1;
618 int top_level_diversion::get_next_page_number()
620 return have_next_page_number ? next_page_number : page_number + 1;
623 void top_level_diversion::set_page_length(vunits n)
625 page_length = n;
628 diversion::~diversion()
632 void page_offset()
634 hunits n;
635 // The troff manual says that the default scaling indicator is v,
636 // but it is in fact m: v wouldn't make sense for a horizontally
637 // oriented request.
638 if (!has_arg() || !get_hunits(&n, 'm', topdiv->page_offset))
639 n = topdiv->prev_page_offset;
640 topdiv->prev_page_offset = topdiv->page_offset;
641 topdiv->page_offset = n;
642 curenv->add_html_tag(0, ".po", n.to_units());
643 skip_line();
646 void page_length()
648 vunits n;
649 if (has_arg() && get_vunits(&n, 'v', topdiv->get_page_length()))
650 topdiv->set_page_length(n);
651 else
652 topdiv->set_page_length(11*units_per_inch);
653 skip_line();
656 void when_request()
658 vunits n;
659 if (get_vunits(&n, 'v')) {
660 symbol s = get_name();
661 if (s.is_null())
662 topdiv->remove_trap_at(n);
663 else
664 topdiv->add_trap(s, n);
666 skip_line();
669 void begin_page()
671 int got_arg = 0;
672 int n;
673 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
674 got_arg = 1;
675 while (!tok.newline() && !tok.eof())
676 tok.next();
677 if (curdiv == topdiv) {
678 if (topdiv->before_first_page) {
679 if (!break_flag) {
680 if (got_arg)
681 topdiv->set_next_page_number(n);
682 if (got_arg || !topdiv->no_space_mode)
683 topdiv->begin_page();
685 else if (topdiv->no_space_mode && !got_arg)
686 topdiv->begin_page();
687 else {
688 /* Given this
690 .wh 0 x
691 .de x
692 .tm \\n%
694 .bp 3
696 troff prints
701 This code makes groff do the same. */
703 push_page_ejector();
704 topdiv->begin_page();
705 if (got_arg)
706 topdiv->set_next_page_number(n);
707 topdiv->set_ejecting();
710 else {
711 push_page_ejector();
712 if (break_flag)
713 curenv->do_break();
714 if (got_arg)
715 topdiv->set_next_page_number(n);
716 if (!(topdiv->no_space_mode && !got_arg))
717 topdiv->set_ejecting();
720 tok.next();
723 void no_space()
725 curdiv->no_space_mode = 1;
726 skip_line();
729 void restore_spacing()
731 curdiv->no_space_mode = 0;
732 skip_line();
735 /* It is necessary to generate a break before before reading the argument,
736 because otherwise arguments using | will be wrong. But if we just
737 generate a break as usual, then the line forced out may spring a trap
738 and thus push a macro onto the input stack before we have had a chance
739 to read the argument to the sp request. We resolve this dilemma by
740 setting, before generating the break, a flag which will postpone the
741 actual pushing of the macro associated with the trap sprung by the
742 outputting of the line forced out by the break till after we have read
743 the argument to the request. If the break did cause a trap to be
744 sprung, then we don't actually do the space. */
746 void space_request()
748 postpone_traps();
749 if (break_flag)
750 curenv->do_break();
751 vunits n;
752 if (!has_arg() || !get_vunits(&n, 'v'))
753 n = curenv->get_vertical_spacing();
754 while (!tok.newline() && !tok.eof())
755 tok.next();
756 if (!unpostpone_traps() && !curdiv->no_space_mode)
757 curdiv->space(n);
758 else
759 // The line might have had line spacing that was truncated.
760 truncated_space += n;
761 curenv->add_html_tag(1, ".sp", n.to_units());
762 tok.next();
765 void blank_line()
767 curenv->do_break();
768 if (!trap_sprung_flag && !curdiv->no_space_mode) {
769 curdiv->space(curenv->get_vertical_spacing());
770 curenv->add_html_tag(1, ".sp", 1);
772 else
773 truncated_space += curenv->get_vertical_spacing();
776 /* need_space might spring a trap and so we must be careful that the
777 BEGIN_TRAP token is not skipped over. */
779 void need_space()
781 vunits n;
782 if (!has_arg() || !get_vunits(&n, 'v'))
783 n = curenv->get_vertical_spacing();
784 while (!tok.newline() && !tok.eof())
785 tok.next();
786 curdiv->need(n);
787 tok.next();
790 void page_number()
792 int n;
794 // the ps4html register is set if we are using -Tps
795 // to generate images for html
796 reg *r = (reg *)number_reg_dictionary.lookup("ps4html");
797 if (r == NULL)
798 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
799 topdiv->set_next_page_number(n);
800 skip_line();
803 vunits saved_space;
805 void save_vertical_space()
807 vunits x;
808 if (!has_arg() || !get_vunits(&x, 'v'))
809 x = curenv->get_vertical_spacing();
810 if (curdiv->distance_to_next_trap() > x)
811 curdiv->space(x, 1);
812 else
813 saved_space = x;
814 skip_line();
817 void output_saved_vertical_space()
819 while (!tok.newline() && !tok.eof())
820 tok.next();
821 if (saved_space > V0)
822 curdiv->space(saved_space, 1);
823 saved_space = V0;
824 tok.next();
827 void flush_output()
829 while (!tok.newline() && !tok.eof())
830 tok.next();
831 if (break_flag)
832 curenv->do_break();
833 if (the_output)
834 the_output->flush();
835 curenv->add_html_tag(1, ".fl");
836 tok.next();
839 void macro_diversion::set_diversion_trap(symbol s, vunits n)
841 diversion_trap = s;
842 diversion_trap_pos = n;
845 void macro_diversion::clear_diversion_trap()
847 diversion_trap = NULL_SYMBOL;
850 void top_level_diversion::set_diversion_trap(symbol, vunits)
852 error("can't set diversion trap when no current diversion");
855 void top_level_diversion::clear_diversion_trap()
857 error("can't set diversion trap when no current diversion");
860 void diversion_trap()
862 vunits n;
863 if (has_arg() && get_vunits(&n, 'v')) {
864 symbol s = get_name();
865 if (!s.is_null())
866 curdiv->set_diversion_trap(s, n);
867 else
868 curdiv->clear_diversion_trap();
870 else
871 curdiv->clear_diversion_trap();
872 skip_line();
875 void change_trap()
877 symbol s = get_name(1);
878 if (!s.is_null()) {
879 vunits x;
880 if (has_arg() && get_vunits(&x, 'v'))
881 topdiv->change_trap(s, x);
882 else
883 topdiv->remove_trap(s);
885 skip_line();
888 void print_traps()
890 topdiv->print_traps();
891 skip_line();
894 void mark()
896 symbol s = get_name();
897 if (s.is_null())
898 curdiv->marked_place = curdiv->get_vertical_position();
899 else if (curdiv == topdiv)
900 set_number_reg(s, nl_reg_contents);
901 else
902 set_number_reg(s, curdiv->get_vertical_position().to_units());
903 skip_line();
906 // This is truly bizarre. It is documented in the SQ manual.
908 void return_request()
910 vunits dist = curdiv->marked_place - curdiv->get_vertical_position();
911 if (has_arg()) {
912 if (tok.ch() == '-') {
913 tok.next();
914 vunits x;
915 if (get_vunits(&x, 'v'))
916 dist = -x;
918 else {
919 vunits x;
920 if (get_vunits(&x, 'v'))
921 dist = x >= V0 ? x - curdiv->get_vertical_position() : V0;
924 if (dist < V0)
925 curdiv->space(dist);
926 skip_line();
929 void vertical_position_traps()
931 int n;
932 if (has_arg() && get_integer(&n))
933 vertical_position_traps_flag = (n != 0);
934 else
935 vertical_position_traps_flag = 1;
936 skip_line();
939 class page_offset_reg : public reg {
940 public:
941 int get_value(units *);
942 const char *get_string();
945 int page_offset_reg::get_value(units *res)
947 *res = topdiv->get_page_offset().to_units();
948 return 1;
951 const char *page_offset_reg::get_string()
953 return i_to_a(topdiv->get_page_offset().to_units());
956 class page_length_reg : public reg {
957 public:
958 int get_value(units *);
959 const char *get_string();
962 int page_length_reg::get_value(units *res)
964 *res = topdiv->get_page_length().to_units();
965 return 1;
968 const char *page_length_reg::get_string()
970 return i_to_a(topdiv->get_page_length().to_units());
973 class vertical_position_reg : public reg {
974 public:
975 int get_value(units *);
976 const char *get_string();
979 int vertical_position_reg::get_value(units *res)
981 if (curdiv == topdiv && topdiv->before_first_page)
982 *res = -1;
983 else
984 *res = curdiv->get_vertical_position().to_units();
985 return 1;
988 const char *vertical_position_reg::get_string()
990 if (curdiv == topdiv && topdiv->before_first_page)
991 return "-1";
992 else
993 return i_to_a(curdiv->get_vertical_position().to_units());
996 class high_water_mark_reg : public reg {
997 public:
998 int get_value(units *);
999 const char *get_string();
1002 int high_water_mark_reg::get_value(units *res)
1004 *res = curdiv->get_high_water_mark().to_units();
1005 return 1;
1008 const char *high_water_mark_reg::get_string()
1010 return i_to_a(curdiv->get_high_water_mark().to_units());
1013 class distance_to_next_trap_reg : public reg {
1014 public:
1015 int get_value(units *);
1016 const char *get_string();
1019 int distance_to_next_trap_reg::get_value(units *res)
1021 *res = curdiv->distance_to_next_trap().to_units();
1022 return 1;
1025 const char *distance_to_next_trap_reg::get_string()
1027 return i_to_a(curdiv->distance_to_next_trap().to_units());
1030 class diversion_name_reg : public reg {
1031 public:
1032 const char *get_string();
1035 const char *diversion_name_reg::get_string()
1037 return curdiv->get_diversion_name();
1040 class page_number_reg : public general_reg {
1041 public:
1042 page_number_reg();
1043 int get_value(units *);
1044 void set_value(units);
1047 page_number_reg::page_number_reg()
1051 void page_number_reg::set_value(units n)
1053 topdiv->set_page_number(n);
1056 int page_number_reg::get_value(units *res)
1058 *res = topdiv->get_page_number();
1059 return 1;
1062 class next_page_number_reg : public reg {
1063 public:
1064 const char *get_string();
1067 const char *next_page_number_reg::get_string()
1069 return i_to_a(topdiv->get_next_page_number());
1072 class page_ejecting_reg : public reg {
1073 public:
1074 const char *get_string();
1077 const char *page_ejecting_reg::get_string()
1079 return i_to_a(topdiv->get_ejecting());
1082 class constant_vunits_reg : public reg {
1083 vunits *p;
1084 public:
1085 constant_vunits_reg(vunits *);
1086 const char *get_string();
1089 constant_vunits_reg::constant_vunits_reg(vunits *q) : p(q)
1093 const char *constant_vunits_reg::get_string()
1095 return i_to_a(p->to_units());
1098 class nl_reg : public variable_reg {
1099 public:
1100 nl_reg();
1101 void set_value(units);
1104 nl_reg::nl_reg() : variable_reg(&nl_reg_contents)
1108 void nl_reg::set_value(units n)
1110 variable_reg::set_value(n);
1111 // Setting nl to a negative value when the vertical position in
1112 // the top-level diversion is 0 undoes the top of page transition,
1113 // so that the header macro will be called as if the top of page
1114 // transition hasn't happened. This is used by Larry Wall's
1115 // wrapman program. Setting before_first_page to 2 rather than 1,
1116 // tells top_level_diversion::begin_page not to call
1117 // output_file::begin_page again.
1118 if (n < 0 && topdiv->get_vertical_position() == V0)
1119 topdiv->before_first_page = 2;
1122 class no_space_mode_reg : public reg {
1123 public:
1124 int get_value(units *);
1125 const char *get_string();
1128 int no_space_mode_reg::get_value(units *val)
1130 *val = curdiv->no_space_mode;
1131 return 1;
1134 const char *no_space_mode_reg::get_string()
1136 return curdiv->no_space_mode ? "1" : "0";
1139 void init_div_requests()
1141 init_request("box", box);
1142 init_request("boxa", box_append);
1143 init_request("bp", begin_page);
1144 init_request("ch", change_trap);
1145 init_request("da", divert_append);
1146 init_request("di", divert);
1147 init_request("dt", diversion_trap);
1148 init_request("fl", flush_output);
1149 init_request("mk", mark);
1150 init_request("ne", need_space);
1151 init_request("ns", no_space);
1152 init_request("os", output_saved_vertical_space);
1153 init_request("pl", page_length);
1154 init_request("pn", page_number);
1155 init_request("po", page_offset);
1156 init_request("ptr", print_traps);
1157 init_request("rs", restore_spacing);
1158 init_request("rt", return_request);
1159 init_request("sp", space_request);
1160 init_request("sv", save_vertical_space);
1161 init_request("vpt", vertical_position_traps);
1162 init_request("wh", when_request);
1163 number_reg_dictionary.define(".a",
1164 new constant_int_reg(&last_post_line_extra_space));
1165 number_reg_dictionary.define(".d", new vertical_position_reg);
1166 number_reg_dictionary.define(".h", new high_water_mark_reg);
1167 number_reg_dictionary.define(".ne",
1168 new constant_vunits_reg(&needed_space));
1169 number_reg_dictionary.define(".ns", new no_space_mode_reg);
1170 number_reg_dictionary.define(".o", new page_offset_reg);
1171 number_reg_dictionary.define(".p", new page_length_reg);
1172 number_reg_dictionary.define(".pe", new page_ejecting_reg);
1173 number_reg_dictionary.define(".pn", new next_page_number_reg);
1174 number_reg_dictionary.define(".t", new distance_to_next_trap_reg);
1175 number_reg_dictionary.define(".trunc",
1176 new constant_vunits_reg(&truncated_space));
1177 number_reg_dictionary.define(".vpt",
1178 new constant_int_reg(&vertical_position_traps_flag));
1179 number_reg_dictionary.define(".z", new diversion_name_reg);
1180 number_reg_dictionary.define("dl", new variable_reg(&dl_reg_contents));
1181 number_reg_dictionary.define("dn", new variable_reg(&dn_reg_contents));
1182 number_reg_dictionary.define("nl", new nl_reg);
1183 number_reg_dictionary.define("%", new page_number_reg);